') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - Jonarod/styled-responsive: Responsive helpers for 💅 styled-components. · GitHub
Skip to content

Repository files navigation

Quick Start

npm install --save styled-components styled-responsive

importreactfrom'react';importstyledfrom'styled-components';import{media,mediaFromProps}from'styled-responsive';constText=styled.span`${media('xs_')` font-size: 1em; padding: 1em; `}${media('sm_')` font-size: 1.3em; padding: 1.3em; `}${media('md_')` font-size: 2em; padding: 2em; `}${(props)=>{returnmediaFromProps({},props)}}`constmyOtherComponent=props=>{conststyles={xs: {margin:'5px',backgroundColor:'papayawhip',fontFamily:'sans-serif',color:'tomato',}}return<Textxs_={{styles.xs}}sm_color='darkseagreen'md_backgroundColor='aliceblue'>Hello</Text>}

Result:

Result of quick start components

Getting Started

The goal is to be able to design anything with responsiveness in mind. So far, we provide two ways:

1. Component side

Here you explictly specify style rules for each breakpoints within your component.

//myComponents/Text.jsimportstyledfrom'styled-components';import{media}from'styled-responsive';constText=styled.span`${media('sm_')` // everything here will apply under the 'sm' breakpoint and up font-size: 1em; `}${media('xl_')` // everything here will apply under the 'xl' breakpoint and up font-size: 2em; `}`exportdefaultText

2. From props

Here you add superpowers to your components: authorizing breakpoint-based style props.

//myComponents/Text.jsimportstyledfrom'styled-components'import{mediaFromProps}from'styled-responsive'constText=styled.span`${(props)=>{returnmediaFromProps({},props)}}`exportdefaultText

Now just use whatever camelcased css and put any of xs_, sm_, md_, lg_ or xl_ in front of it: properties will apply according to each breakpoint.

For example:

  • xs_backgroundColor:'red' will apply backgroundColor:'red' from the xs_ breakpoint and up.
  • sm_display:'none' will apply display:'none' from the sm_ breakpoint and up.
  • md_fontSize:'1em' will apply fontSize:'1em' from the md_ breakpoint and up.
  • lg_paddingRight:'5px' will apply paddingRight:'5px' from the lg_ breakpoint and up.
  • xl_minHeight:'300px' will apply minHeight:'300px' from the xl_ breakpoint and up.

...got it?!

Wait... you can also provide an object to any of these prefixed props. For example:

  • xs_={{backgroundColor:'red', fontSize:'1em', padding:'1em'}}
  • sm_={{backgroundColor:'green', fontSize:'2em', padding:'2em'}}
  • md_={{backgroundColor:'blue', fontSize:'3em', padding:'3em'}}
  • lg_={{backgroundColor:'pink', fontSize:'4em', padding:'4em'}}
  • xl_={{backgroundColor:'orange', fontSize:'5em', padding:'5em'}}

Obviously, you can combine both at will. So this is valid:

<MyComponent xs_={{backgroundColor:'red', fontSize:'1em', padding:'1em'}} xs_fontSize='3em' />

As in the style prop, the last property always replaces previous ones. This means that, in our example, fontSize will be set to the latest assigned value: xs_fontSize='3em'.

Back to our Text component, we could consume it like this:

//OtherComponent.jsimportReact,{Component}from'react'importTextfrom'./myComponents/Text.js'classOtherComponentextendsComponent{//...render(){conststyles={medium: {backgroundColor:'pink'}}return(<Textxs_backgroundColor='red'sm_={{backgroundColor:'white'}}md_={styles.medium}lg_backgroundColor='green'xl_backgroundColor='blue'/>)}}
`

Here, Text will display a span with a...

  • ...red background on xs_ breakpoint and up
  • ...white background on sm_ breakpoint an up
  • ...pink background on md_ breakpoint an up
  • ...green background on lg_ breakpoint an up
  • ...blue background on xl_ breakpoint an up

Customizing Breakpoints

styled-responsive comes with most common breakpoints:

constdefaultBreakpoints={xs_:'only screen and (min-width: 0px)',sm_:'only screen and (min-width: 576px)',md_:'only screen and (min-width: 768px)',lg_:'only screen and (min-width: 992px)',xl_:'only screen and (min-width: 1200px)'}

Now, who are we to impose those to you ?! Just override it ! You can easily apply your own media queries by creating a similar object like:

//myBreakpoints.jsexportconstmyBreakpoints={phones_: 'only screen and (min-width: 0px)',tablets_: 'only screen and (min-width: 400px)',desktops_: 'only screen and (min-width: 800px)'}

Note: all standard media queries are available to you, so you are free to define more complex queries matching your use-cases like portrait, max-width, min-height etc...

Now, you can provide it to media() :

importmyBreakpointsfrom'./myBreakpoints.js'constText=styled.span`${media('phones_',myBreakpoints)` // everything here will apply under the 'phones_' breakpoint and up font-size: 1em; `}${media('desktops_',myBreakpoints)` // everything here will apply under the 'desktops_' breakpoint and up font-size: 2em; `}`

or mediaFromProps() :

importmyBreakpointsfrom'./myBreakpoints.js'constText=styled.span`${(props)=>{returnmediaFromProps({breakpoints:myBreakpoints},props)}}`

Obviously, if you change breakpoints names, your responsive props should be prefixed accordingly. In our example, we now should use prefixes phones_, tablets_ and desktops_ instead of the default ones (xs_, sm_, md_ etc...), like:

<MyComponent phones_={{backgroundColor:'red', fontSize:'1em', padding:'1em'}} desktops_fontSize='3em' />

Shortcuts

We are really lazy... so we created shortcuts properties that just translates into classic [longer] properties. This enables us to write p:'10px' which will translate to padding:'10px', or mb:'5px' which is the same as writing marginBottom:5px... Thinking this way, we also ship react-native's Vertical and Horizontal helpers for a few classic properties like padding, margin and border. So you can write mh:'auto' which is strictly the same as writing marginLeft:'auto', marginRight:'auto'. ...cool, right ?!

Here is the complete shortcuts list:

ShortcutTranslates into...ExampleTranslated example:
wwidthw:'100px'width:'100px'
hheighth:'100px'height:'100px'
minWminWidthminW:'100px'minWidth:'100px'
minHminHeightminH:'100px'minHeight:'100px'
maxWmaxWidthmaxW:'100px'maxWidth:'100px'
maxHmaxHeightmaxH:'100px'maxHeight:'100px'
bgbackgroundbg:'red'background:'red'
bgColorbackgroundColorbgColor:'red'backgroundColor:'red'
wrapflexWrap:'wrap'wrapflexWrap:'wrap'
nowrapflexWrap:'nowrap'nowrapflexWrap:'nowrap'
growflexGrow:1grow:{2}flexGrow:2
shrinkflexShrink:1shrink:{2}flexShrink:2
basisflexBasis:'auto'basis:{1/3}flexBasis:'33.3333333%'
basis:'100px'flexBasis:'100px'
basis:'50%'flexBasis:'50%'
basis:{0}flexBasis:0
rowflexDirection: 'row'rowflexDirection: 'row'
columnflexDirection: 'column'columnflexDirection: 'column'
scrolloverflow: 'auto'scrolloverflow: 'auto'
noscrolloverflow: 'hidden'noscrolloverflow: 'hidden'
hidedisplay: 'none'hidedisplay: 'none'
showdisplay: 'flex'showdisplay: 'flex'
relativeposition: 'relative'relativeposition: 'relative'
absoluteposition: 'absolute'absoluteposition: 'absolute'
alignStartalignItems: 'flex-start'alignStartalignItems: 'flex-start'
alignCenteralignItems: 'center'alignCenteralignItems: 'center'
alignEndalignItems: 'flex-end'alignEndalignItems: 'flex-end'
alignStretchalignItems: 'stretch'alignStretchalignItems: 'stretch'
alignMeStartalignSelf: 'flex-start'alignMeStartalignSelf: 'flex-start'
alignMeCenteralignSelf: 'center'alignMeCenteralignSelf: 'center'
alignMeEndalignSelf: 'flex-end'alignMeEndalignSelf: 'flex-end'
alignMeStretchalignSelf: 'stretch'alignMeStretchalignSelf: 'stretch'
justifyStartjustifyContent: 'flex-start'justifyStartjustifyContent: 'flex-start'
justifyCenterjustifyContent: 'center'justifyCenterjustifyContent: 'center'
justifyEndjustifyContent: 'flex-end'justifyEndjustifyContent: 'flex-end'
justifySpaceAroundjustifyContent: 'space-around'justifySpaceAroundjustifyContent: 'space-around'
justifySpaceBetweenjustifyContent: 'space-between'justifySpaceBetweenjustifyContent: 'space-between'
wrapStartjustifyItems: 'flex-start'wrapStartjustifyItems: 'flex-start'
wrapCenterjustifyItems: 'center'wrapCenterjustifyItems: 'center'
wrapEndjustifyItems: 'flex-end'wrapEndjustifyItems: 'flex-end'
wrapSpaceAroundjustifyItems: 'space-around'wrapSpaceAroundjustifyItems: 'space-around'
wrapSpaceBetweenjustifyItems: 'space-between'wrapSpaceBetweenjustifyItems: 'space-between'
ppaddingp:'5px'padding:'5px'
ptpaddingToppd:'5px'paddingTop:'5px'
prpaddingRightpr:'5px'paddingRight:'5px'
pbpaddingBottompb:'5px'paddingBottom:'5px'
plpaddingLeftpl:'5px'paddingLeft:'5px'
pvpaddingTop + paddingBottompv:'5px'paddingTop:'5px', paddingBottom:'5px'
phpaddingLeft + paddingRightph:'5px'paddingLeft:'5px', paddingRight:'5px'
paddingVerticalpaddingTop + paddingBottompaddingVertical:'5px'paddingTop:'5px', paddingBottom:'5px'
paddingHorizontalpaddingLeft + paddingRightpaddingHorizontal:'5px'paddingLeft:'5px', paddingRight:'5px'
mmarginm:'5px'margin:'5px'
mtmarginTopmd:'5px'marginTop:'5px'
mrmarginRightmr:'5px'marginRight:'5px'
mbmarginBottommb:'5px'marginBottom:'5px'
mlmarginLeftml:'5px'marginLeft:'5px'
mvmarginTop + marginBottommv:'5px'marginTop:'5px', marginBottom:'5px'
mhmarginLeft + marginRightmh:'5px'marginLeft:'5px', marginRight:'5px'
marginVerticalmarginTop + marginBottommarginVertical:'5px'marginTop:'5px', marginBottom:'5px'
marginHorizontalmarginLeft + marginRightmarginHorizontal:'5px'marginLeft:'5px', margin:'5px'
bborderWidthb:'5px'borderWidth:'5px'
btborderTopWidthbd:'5px'borderTopWidth:'5px'
brborderRightWidthbr:'5px'borderRightWidth:'5px'
bbborderBottomWidthbb:'5px'borderBottomWidth:'5px'
blborderLeftWidthbl:'5px'borderLeftWidth:'5px'
bvborderTopWidth + borderBottomWidthbv:'5px'borderTopWidth:'5px', borderBottomWidth:'5px'
bhborderLeftWidth + borderRightWidthbh:'5px'borderLeftWidth:'5px', borderRightWidth:'5px'

Overriding shortcuts

We don't think miliseconds lost for shortcut processing are a big pain when compared to gain in code readibility... however, for purists who just want to pass their way on this one, we provide options to override it.

1. overriding it completely: meaning "don't do it".

In the mediaFromProps() function, just specify it like this:

importmyBreakpointsfrom'./myBreakpoints.js'constText=styled.span`${(props)=>{returnmediaFromProps({breakpoints:myBreakpoints,translatorFunc:false},props)}}`

Note how translatorFunc:false is part of the same object as {breakpoints}.

2. overriding using your own sbortcuts: meaning "I prefer mine".

In the mediaFromProps() function, just specify it like this:

importmyBreakpointsfrom'./myBreakpoints.js'importmyFuncfrom'./myFunc.js'constText=styled.span`${(props)=>{returnmediaFromProps({breakpoints:myBreakpoints,translatorFunc:myFunc},props)}}`

Note how translatorFunc:myFunc is part of the same object as {breakpoints}.

You can copy our mapTranslator.js file as a starter, and add/remove properties you need.

Todo

  • test with react-native
  • write unit-tests

About

Responsive helpers for 💅 styled-components.

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages