Getting started
Add a few tags to your <head> and start using the .joy-* classes. Works in any stack: plain HTML, PHP, React, Vue, whatever you're running.
The snippets on this page are for pasting into your own project's <head> or before </body>. They're shown as code only, not executed on this page — running a second copy of the theme link or icon script here would conflict with the ones this site already loads.
1. External dependencies
Joy ships no font or icon set of its own. It expects these two things to already be loaded on the page before its own files:
| Dependency | Why |
|---|---|
| Google Fonts Poppins | Used everywhere in the system (--joy-font). |
| Phosphor Icons (web script) | Every Joy icon is an <i class="ph ph-*"> tag — without the script, icons render blank. |
2. The tags
Add this to your <head>, in this order — the tokens file needs to load before the rest:
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <script src="https://unpkg.com/@phosphor-icons/web"></script> <link rel="stylesheet" href="https://cdn.vaneltonmedia.com/joy/styles/joy-tokens.css"> <link rel="stylesheet" href="https://cdn.vaneltonmedia.com/joy/styles/joy.css"> <link rel="stylesheet" href="https://cdn.vaneltonmedia.com/joy/styles/joy-utilities.css"> <link id="joy-dark-theme" rel="stylesheet" href="https://cdn.vaneltonmedia.com/joy/styles/joy-dark.css" disabled>
Before </body>, if you want the JS components (toast, modal, cropper, theme, loading button):
<script src="https://cdn.vaneltonmedia.com/joy/joy.js"></script>
The four CSS files are independent: joy-tokens.css defines the variables, joy.css has the components, joy-utilities.css has the Tailwind-style utility classes, and joy-dark.css is the dark theme (optional — only include it if you're offering dark mode). None of them need a bundler, PostCSS, or any preprocessor.
3. Wire up dark mode (optional)
joy-dark.css only overrides variables. It stays loaded but disabled, and gets turned on/off by flipping that attribute. Add this script right after the <link id="joy-dark-theme"> tag to avoid a flash of the wrong theme on load:
<script>(function(){var t=localStorage.getItem('selectedTheme')||(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light');if(t==='dark'){document.getElementById('joy-dark-theme').disabled=false;}})();</script>Once joy.js is loaded, any button with id="joy-theme-toggle" anywhere on the page toggles the theme on its own — see Theme & tokens.
4. Use only what you need
This is what sets Joy apart from most design systems: every piece of JS checks that its target element exists before doing anything. There's no required init call, and nothing errors in the console if a piece is missing.
- Want a nicer confirmation dialog? Load
joy.jsand callJoyModal.confirm(...)— no topbar, dock, or theme setup required. - Want just the layout utilities (
.joy-flex,.joy-grid-3...)? Loadjoy-tokens.css+joy-utilities.cssand skip the rest. - Want the full look (glass topbar, bottom dock)? Build the markup described in Navigation.
5. SPAs and traditional sites both work
Joy doesn't assume any routing framework. The CSS classes work the same on every page. The JS components (initReveal, initDock, theme...) run automatically on DOMContentLoaded. If your site swaps content via fetch/pushState instead of reloading, dispatch a custom spa:navigated event on window after you swap the HTML, so Joy re-runs entry animations (.joy-reveal) and theme state on the new content:
window.dispatchEvent(new CustomEvent('spa:navigated', { detail: { url: newUrl } }));Next
Head to Theme & tokens to customize the colors for your brand, or jump straight into Buttons and start copying components.