Getting started

Theming

Light and dark are built in. Everything else — accent, radius, type — is a CSS custom property away.

Light and dark #

Light is the default. Put class="dark" or data-theme="dark" on any ancestor — usually <html> — and everything below switches. Themes can be scoped, too:

Copy
<section data-theme="dark">
  <!-- Everything in here renders dark, whatever the page uses. -->
</section>

Dark mode isn’t an inversion. Surfaces step up in lightness as they rise (page → surface → raised), borders carry more of the structure, and shadows get deeper to stay visible.

Avoid the flash #

To render the right theme on first paint, set the class from an inline script in <head> before the page draws. This site does exactly that:

nuxt.config.ts
Copy
// nuxt.config.ts — set the class before first paint
const themeScript = `(function(){try{
  var t = localStorage.getItem('theme') || 'system';
  var dark = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
  document.documentElement.classList.toggle('dark', dark);
}catch(e){}})()`

export default defineNuxtConfig({
  app: {
    head: { script: [{ innerHTML: themeScript, tagPosition: 'head' }] },
  },
})

Customising #

Every visual decision is a token prefixed with --juxt-. Override them after juxt.ui’s stylesheet, globally or on any subtree. Components read tokens at runtime, so there is nothing to rebuild.

theme.css
Copy
:root {
  /* A warmer accent */
  --juxt-accent: #0e8f76;
  --juxt-accent-hover: #0c7c66;
  --juxt-accent-text: #0a6b58;
  --juxt-ring: rgb(14 143 118 / 0.55);
  --juxt-ring-soft: rgb(14 143 118 / 0.16);

  /* Slightly rounder controls */
  --juxt-radius-sm: 0.5rem;
  --juxt-radius-md: 0.625rem;
}

.dark {
  --juxt-accent-text: #2fcfa9;
}

When you change the accent, change its -text variant too: it is tuned separately per theme so text stays above WCAG AA contrast on both backgrounds.

See Design tokens for the full list.