scoped-style
v1.9.0
Published
Scoped style is a next gen tiny css-in-js library to help you style your components. Use the full power of css that you are used to.
Downloads
37
Readme
Scoped Style
Scoped style is a next gen tiny css-in-js library to help you style your components. Use the full power of css that you are used to.
Works With
Installation
npm i scoped-style
// or
yarn add scoped-style
Usage
import scoped from 'scoped-style';
// for react
import React from 'react';
const styled = scoped(React.createElement);
//
// for Preact
import { h } from 'preact';
const styled = scoped(h);
//
// for Hyperapp
import { h } from 'hyperapp';
const styled = scoped(h);
//
// for Infernojs
import { createElement } from 'inferno-create-element';
const styled = scoped(createElement);
//
// define global css
styled.global`
* {
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
}
`;
// and scoped css
const Button = styled('button')`
background: ${props => (props.primary ? 'orange' : 'gray')};
border: none;
border-radius: 2px;
:hover,
:focus,
:active {
padding: 10px;
}
@media screen and (max-width: 640px) {
background: blue;
:hover,
:focus,
:active {
padding: 5px;
}
}
`;
// keyframes
const spin = styled.keyframes`
to {
transform: rotate(360deg);
}
`;
const Loader = styled('div')`
border: 3px solid hsla(185, 100%, 62%, 0.2);
border-top-color: #3cefff;
border-radius: 50%;
width: 2em;
height: 2em;
animation: ${() => spin} 1s linear infinite;
`;
// styling children
const BlueChildren = styled('div')`
> * {
color: blue;
}
`;
const App = ({ children }) => (
<div>
<Button primary>Login</Button>
<Loader />
<BlueChildren>{children}</BlueChildren>
</div>
);
// Your rendering code
Configuration
generateID - application code
Scoped style lets you choose the way class names are generated.
This is possible via generateID
method which can be replaced by your own implementation:
// <componentName>.styles.js
import { h } from 'preact';
import scoped from 'scoped-style';
// These two lines below are responsible for achieving custom class name generation
const defaultGenerateID = scoped.generateID;
scoped.generateID = () => `my-app-prefix-${defaultGenerateID()}`;
const styled = scoped(h);
export const Container = styled('section')`
padding: 10px;
color: #000;
`;
export const AppDescription = styled('h1')`
color: #0f0f0f;
`;
Having added that you should end up with something like this:
Support and limitations
Combinators
Supported :
Not supported :
Selectors
Selectors must be used in with a combinator.
We support them all :
Pseudo selectors
We support them all.
Warning, if you are using the content
property : special characters (like ◀
) are not suppoted, you must convert them (via http://enc.joyho.net/ for example) and it will work (content: "\\25C0";
).
Questions, bugs and feature requests
You have a question about usage, feel free to open an issue.
You found a bug, feel free to open an issue.
You've got a feature requests, feel free to open an issue.
Advanced example : radio button, checkbox
const Label = styled('label')`
position: relative;
padding-left: 2rem;
padding-right: 0.75rem;
margin-bottom: 0.75rem;
cursor: pointer;
font-size: 1rem;
user-select: none;
> input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
> input[type='checkbox'] ~ span {
position: absolute;
top: 0.2rem;
left: 0;
height: 1rem;
width: 1rem;
background-color: #eee;
}
> input[type='radio'] ~ span {
position: absolute;
top: 0.2rem;
left: 0;
height: 1rem;
width: 1rem;
background-color: #eee;
border-radius: 50%;
}
:hover > input ~ span {
background-color: #ccc;
transition: 0.2s;
}
> input:checked ~ span {
background-color: #0080b3;
}
:active > span {
transform: scale(0);
}
> span:after {
content: '';
position: absolute;
display: none;
}
> input:checked ~ span:after {
display: block;
}
> input[type='checkbox'] ~ span:after {
left: 0.25rem;
top: 0.05rem;
width: 0.25rem;
height: 0.6rem;
border: solid white;
border-width: 0 0.2rem 0.2rem 0;
transform: rotate(45deg);
}
> input[type='radio'] ~ span:after {
left: 0.3rem;
top: 0.3rem;
width: 0.4rem;
height: 0.4rem;
border-radius: 50%;
background: white;
}
`;
const Radio = ({ name, checked, onChange }) => (
<Label>
{name}
<input type="radio" checked={checked} onChange={onChange} />
<span></span>
</Label>
);
const Checkbox = ({ name, checked, onChange }) => (
<Label>
{name}
<input type="checkbox" checked={checked} onChange={onChange} />
<span></span>
</Label>
);
Scoped function second parameter
import scoped from 'scoped-style';
scoped
can take a second parameter : a callback who "render" the css generated by styled
function.
The default callback is exported via scoped.defaultCallback
.
SSR example
Client :
import { createElement } from 'inferno-create-element';
import scoped from './scoped-style';
if (typeof global !== 'undefined') {
global.scopedStyleCSS == '';
}
const styler = css => {
if (typeof document !== 'undefined') {
scoped.defaultCallback(css);
} else if (typeof global !== 'undefined') {
global.scopedStyleCSS += css;
}
};
export const styled = scoped(createElement, styler);
Server :
app.get('*', (req, res) => {
// first render the root component to string via the SSR function of your framework
const content = renderToString(<App {...props} />);
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
<meta content="text/html; charset=utf-8">
<title>
Citral
</title>
<style type="text/css">
${global.scopedStyleCSS /* then use the generated styles string */}
</style>
</head>
<body>
${content}
<script type="text/javascript" src="${fs
.readdirSync(path.join(__dirname, 'client'))
.find(file => /^[a-z0-9]+\.bundle\.js$/.test(file))}"></script>
</body>
</html>
`);
});