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
clsx
handling
😰 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>
Install the package depending on your framework:
npm install class-variant
import cv from 'class-variant'
const btn = cv(...params)
const btn = cv`...` // or
btn(props) // -> string
npm install @master/styled.react
import styled from '@master/styled.react'
const Button = styled.button(...params)
const Button = styled.button`...` // or
<Button {...props}>
npm install @master/styled.vue
import styled from '@master/styled.vue'
const Button = styled.button(...params)
const Button = styled.button`...` // or
<Button {...props}>
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>
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>
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>
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>
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.
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 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>
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'
})
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
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.
Please see our CONTRIBUTING for workflow.
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.