Features
Vanilla, Next, React, Vue, Tailwind CSS, and Master CSS are available:
- β‘οΈ Ultra-lightweight ~1.5KB, powered by
Proxy - π‘οΈ Type safety
- π Dynamically change styles based on properties
- π« Re-expand existing elements, like
NextLink - π§© Compatible with server and client components
- πͺ Built-in first-class
clsxhandling
Why?
π° Before: Creating a simple styled element using a FunctionalComponent is unpleasant.
function Button(props) { return ( <button {...props} className={"inline-flex font:14" + (props.className ? ' ' + props.className : '')}> {props.children} </button> ) }
π₯³ After: It's in one line and ~80% code less.
import styled from '@master/styled.react' // or .vue const Button = styled.button`inline-flex font:14`
Apply it as usual:
export default function App() { return ( <Button className="uppercase">submit</Button> ) }
It will be rendered as:
<button class="inline-flex font:14 uppercase">submit</button>
Getting Started
Install the package depending on your framework:
Vanilla
npm install class-variant
import cv from 'class-variant' const btn = cv(...params) const btn = cv`...` // or btn(props) // -> string
React
npm install @master/styled.react
import styled from '@master/styled.react' const Button = styled.button(...params) const Button = styled.button`...` // or <Button {...props}>
Vue
npm install @master/styled.vue
import styled from '@master/styled.vue' const Button = styled.button(...params) const Button = styled.button`...` // or <Button {...props}>
Basic usage
Create a styled element
Declared in two ways: function or template literal, and parameters inherit all features of clsx.
const Element = styled.div`flex text:center` const Element = styled.div('flex text:center') // or return ( <Element className="uppercase">Hello World</Element> )
<div class="flex text:center uppercase">Hello World</div>
Apply based on predefined variants
Predefine the class variant corresponding to the property.
const Button = styled.button('flex', { $size: { sm: 'font:12 size:8x', md: 'font:14 size:12x' }, disabled: 'opacity:.5', ... }) return ( <Button $size="sm" disabled /> )
<button class="flex font:12 size:8x opacity:.5" disabled></button>
β οΈ Custom properties that are not element-owned properties must be prefixed with
$prop, otherwise they will be reflected on the final element and an error may be thrown.
You can set default properties for elements.
Button.default = { $size: 'md' } return ( <Button /> )
<button class="font:14 size:12x"></button>
Apply based on function properties
Dynamically apply class names based on function properties.
const Element = styled.div('fg:white', ({ $color }) => $color && `bg:${$color}` ) return ( <Element $color="blue" /> )
<div class="inline-flex text:center fg:white bg:blue"></div>
Apply based on matching properties
Applies the target class name matching all specified property keys and their values.
const Button = styled.button('inline-flex', ['uppercase', { $intent: 'primary', $size: 'md' }] ) return ( <Button $intent="primary">Not matched</button> <Button $size="md">Not matched</button> <Button $intent="primary" $size="md">Matched</button> )
<button class="inline-flex">Not matched</button> <button class="inline-flex">Not matched</button> <button class="inline-flex uppercase">Matched</button>
Extended
Extend a styled element
Extend an existing styled element and add additional classes or conditions.
const A = styled.p('a') const B = styled.p(A)`b` return ( <A>A</A> <B>B</B> )
<p class="a">A</p> <p class="a b">B</p>
It also supports client components, taking Next.jsβs Link as an example:
'use client' import styled from '@master/styled.react'; import Link from 'next/link'; const Button = styled(Link)`inline-flex text:center` return ( <Button>Submit</Button> )
β οΈ If the extended target contains client components, the
'use client'directive is required.
Change an element's tag name
Changing the original tag name of an element, such as <button> to <a>. Left empty '' even if there are no additional classes.
const Button = styled.button('inline-flex') const Link = styled.a(Button)`` return ( <Button>button</Button> <Link href="#example">link</Link> )
<button class="inline-flex">button</button> <a class="inline-flex" href="#example">link</a>
Extend multiple styled elements
Extend multiple style elements at once.
const A = styled.p`a` const B = styled.p`b` const C = styled.p`c` const D = styled(A)(B)(C)`d` return ( <A>A</A> <B>B</B> <C>C</C> <D>D</D> )
<p class="a">A</p> <p class="b">B</p> <p class="c">C</p> <p class="a b c d">D</p>
Typings
declare type Props = { $size: 'sm' | 'md' } const Button = styled.button<Props>('flex', { $size: { sm: 'font:12 size:8x', md: 'font:14 size:12x' }, disabled: 'opacity:.5' })
Overview class-variant APIs
import cv from 'class-variant' const btn = cv( 'inline-flex rounded', { intent: { primary: 'bg:blue fg:white bg:blue-60:hover', secondary: 'bg:white fg:slate-30 bg:slate-90:hover', }, size: { sm: 'text:14 p:5|15', md: 'text:16 p:10|25' } }, ['uppercase', { intent: 'primary', size: 'md' }], ({ indent, size }) => indent && size && 'font:semibold' ) btn.default = { intent: 'primary', size: 'sm' } btn() // inline-flex rounded bg:blue fg:white bg:blue-55:hover text:14 p:5|8 font:semibold btn({ indent: 'secondary', size: 'sm' }) // inline-flex rounded bg:white fg:slate-30 bg:slate-90:hover text:14 p:5|8 font:semibold btn({ indent: 'primary', size: 'md' }) // inline-flex rounded bg:blue fg:white bg:blue-55:hover text:16 p:10|25 uppercase font:semibold
Community
The Master community can be found here:
- Discuss on GitHub - Ask questions, voice ideas, and do any other discussion
- Join our Discord Server - Casually chat with other people using the language β δΈζ
Our γ Code of Conduct γ applies to all Master community channels.
Contributing
Please see our CONTRIBUTING for workflow.
Inspiration
Some of our core concepts and designs are inspired by these giants.
- Template Literal - The use of template literals as syntactic sugar for reusing components is inspired by Styled Components.