Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork49
Create visual components for React without JavaScript or TypeScript. Leverage native HTML and CSS. It's an alternative to CSS-in-JS and CSS modules.
License
typicode/mistcss
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Simplicity is the ultimate sophistication
MistCSS lets you create reusable visual components without JavaScript or TypeScript (think about it for a second... no JS/TS needed).
Leverage native HTML and CSS, get type safety and autocomplete. Just clean and efficient styling.

What you see above is standard HTML (data-attributes) and CSS (nested CSS). MistCSS simply creates ad.ts file based on your CSS.
- 🥶 Not just zero-runtime, it goes beyond. It's zero JavaScript, not even for components, resulting in smaller bundles and faster code.
- 💎 What you write is what you get. No transformations, easy debugging.
- 🎒 Standards-based, reusable styles across frameworks, compatible with Tailwind or any CSS framework
- ⚡️ Instantly productive, no learning curve, simple on-boarding.
- 💖 Back to basics with a modern twist: access the full power of HTML and CSS, enhanced with type safety and code completion (without the complexity).
| CSS-in-JS | MistCSS | |
|---|---|---|
| Runtime | ~0-10 KB | 0 KB |
| JavaScript functions | a few KB per component | 0 KB |
| TypeScript code | yes (at least for props) | no (generated for the user) |
| Debugging | react devtools | browser inspector |
| Syntax highlighting | depends (may require extension) | no additional extension |
| Generated bundle | runtime + JS functions + logic + CSS | CSS |
This is general comparison and may vary depending on the library you're using.
Traditional approaches require wrapping your markup/styles in JavaScript functions (Button.tsx →<button/>,Input.tsx →<input/>, ...), defining props with TypeScript types, and writing logic to manage class names.
With MistCSS, styling is straightforward and minimal. Here’s how it looks:
mist.css
button {border-radius:1rem;padding:1rem;background: lightgray;&[data-variant='primary'] {background-color: black;color: white; }&[data-variant='secondary'] {background-color: grey;color: white; }}
Page.tsx
<><buttondata-variant="primary">Save</button>{/* TS error, tertiary isn't valid */}<buttondata-variant="tertiary">Save</button></>
Output
<buttondata-variant="primary">Save</button>{/* Same as in Page.tsx */}
This example demonstrates enums, but MistCSS also supports boolean and string props. For more details, see the FAQ.
MistCSS parses yourmist.css file and generatesmist.d.ts for type safety.
For instance, here’s the generatedmist.d.ts for our button component:
interfaceMist_buttonextendsReact.DetailedHTMLProps<React.HTMLAttributes<HTMLButtonElement>,HTMLButtonElement>{'data-variant'?:'primary'|'secondary'}declarenamespaceJSX{interfaceIntrinsicElements{button:Mist_button// ← <button/> is extended at JSX level to allow 'primary' and 'secondary' values}}
That’s it! Simple yet powerful, built entirely on browser standards and TypeScript/JSX.
npm install mistcss --save-dev
postcss.config.js
module.exports={plugins:{mistcss:{},},}
layout.tsx
import'./mist.css'
Absolutely, MistCSS is pure HTML and CSS, generating onlymist.d.ts, so there are no limitations. You can integrate any CSS framework seamlessly. Here are a few examples to get you started:
Important
For the best experience, set up Tailwind IntelliSense in your editor. Refer toTailwind's editor setup guide.
Tailwind v3 (@apply)
button {@apply bg-blue-500 text-white;/* ... */}
Tailwind v3 (theme)
button {background:theme(colors.blue.500);/* ... */}
Tailwind v4 will support CSS variables natively (seeblog post).
To override some styles, you can useclassName
<buttondata-variant="primary"className="p-12"> Save</button>
button {background-color:var(--blue-6);/* ... */}
CSS is more powerful than ever, before reaching for JS, explore if native CSS features can accomplish what you need.
No, using<name> would result in invalid HTML. However, this constraint is actually advantageous.
Firstly, it eliminates the risk of conflicts with native attributes:
<><Buttontype="primary">Save</Button{/* Conflict with button's type="submit" */}<buttondata-type="primary">Save</button>{/* Safe */}</>
Additionally, just by typingdata- in your editor, autocomplete helps you clearly distinguish your custom attributes from standard tag attributes.
div[data-component='section']/* CSS variables */--color: ...;/* Default styles */ background: var(--color,green); margin: ...; padding: ...;/* Enum props */&[data-size="sm"] { ... }&[data-size="lg"] { ... }/* Boolean props */&[data-is-active] { ... }/* Condition: size="lg" && is-active */&[data-size="lg"]&[data-is-active] { ... }/* Condition: size="lg" && !is-active */&[data-size="lg"]:not([data-is-active]) { ... }}
<divdata-component="section"data-size="foo"data-is-activestyle={{'--color':'red'}}/>
If you want both basic links and button-styled links, here’s how you can do:
a:not([data-component]) {/* ... */ }a[data-component='button'] {&[data-variant='primary'] {/* ... */ }}
<><ahref="/home">Home</a><ahref="/home"data-component="button">Home</a><ahref="/home"data-component="button"data-variant="primary">Home</a>{/* TS error, `data-variant` is only valid with `data-component="button"` */}<ahref="/home"data-variant="primary">Home</a></>
Note
data-component is just a naming convention. Feel free to use any attribute, likedata-kind='button' or justdata-c. It’s simply a way to differentiate between components using the same tag.
You can use CSS@import. For example, in yourmist.css file:
@import'./button.css';
mist.css
article[data-component='card'] {/* ... */}div[data-component='card-title'] {/* ... */}div[data-component='card-content'] {/* ... */}
Card.jsx
exportfunctionCard({ title, children}){return(<articledata-component="card"><divdata-component="card-title">{title}</div><divdata-component="card-content">{children}</div></article>)}
Tip
To indicate that these styles aren't meant to be used outside ofCard, you can name themdata-p-component (p forprivate) or use another naming convention.
:root {--primary-color:#007bff;--secondary-color:#6c757d;}button {background:var(--primary-color)/* ... */
See also your CSS framework/tooling documentation for ways to define them in JS if you prefer.
Assuming you have your UI components in a separate packagemy-ui and you're using Next.js, follow these steps:
app/layout.tsx
import'my-ui/mist.css'
app/mist.d.ts
import 'my-ui/mist.d.tsThis setup ensures that your Next.js application correctly imports styles and type definitions from your external UI package. It may vary based on tools you're using, but the same principles should apply.
Mist is inspired by atomized water 💧 often seen near waterfalls. A nod to theCascading in CSS 🌊.
About
Create visual components for React without JavaScript or TypeScript. Leverage native HTML and CSS. It's an alternative to CSS-in-JS and CSS modules.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors11
Uh oh!
There was an error while loading.Please reload this page.