llms.txt
llms.txt

Themes

FARMJS can manage a visitor's light, dark, or system preference without owning your colors. The framework applies the active mode before styles load, persists the preference, and exposes typed client and server APIs. Tailwind utilities, CSS variables, or ordinary selectors still define how the application looks.

Enable themes

Add theme to the application config:

farm.config.ts
import { defineConfig } from "@farm.js/core";export default defineConfig({  theme: {    default: "system",    storageKey: "farm-theme",  },});

default accepts "light", "dark", or "system". Theme support is opt-in; omit the property or set it to false when the application manages color mode itself.

FARMJS stores the preference in a same-site cookie so server rendering can read it. It mirrors changes to local storage for cross-tab updates. The cookie path follows the configured basePath.

Tailwind dark variants

When the built-in Tailwind integration processes a stylesheet containing @import "tailwindcss", FARMJS connects the dark: variant to its data-theme selector automatically:

export function Panel() {  return (    <section className="bg-white text-neutral-950 dark:bg-black dark:text-white">      Theme-aware content    </section>  );}

The active document is either &lt;html data-theme=&quot;light&quot;&gt; or &lt;html data-theme=&quot;dark&quot;&gt;. If the stylesheet already defines @custom-variant dark, FARMJS preserves that definition instead of overriding it.

Plain CSS and design tokens

Tailwind is optional. Use the same selector with CSS variables or ordinary styles:

:root {  --background: #ffffff;  --foreground: #0a0a0a;}[data-theme="dark"] {  --background: #000000;  --foreground: #f5f5f5;}body {  background: var(--background);  color: var(--foreground);}

FARMJS also sets color-scheme for the resolved mode so native form controls and browser surfaces match the page.

Read and change the theme

Use useTheme in a client component:

"use client";import { useTheme } from "@farm.js/core/theme/client";export function ThemePicker() {  const { theme, resolvedTheme, mounted, setTheme } = useTheme();  return (    <div role="group" aria-label="Color theme">      {(["light", "dark", "system"] as const).map((option) => (        <button          key={option}          type="button"          aria-pressed={theme === option}          onClick={() => setTheme(option)}        >          {option}        </button>      ))}      <span aria-live="polite">        {mounted && resolvedTheme ? `Using ${resolvedTheme} mode` : "Resolving theme"}      </span>    </div>  );}

The returned values have different jobs:

  • theme is the saved &quot;light&quot;, &quot;dark&quot;, or &quot;system&quot; preference.
  • resolvedTheme is the active &quot;light&quot; or &quot;dark&quot; browser mode. It is undefined during server rendering when the saved preference is &quot;system&quot;.
  • mounted becomes true when the browser runtime is active.
  • setTheme(theme) saves and applies a preference.
  • toggleTheme() switches between the resolved light and dark modes.

The client module also exports non-hook getTheme, setTheme, and toggleTheme functions for event handlers or stores outside React components.

Read the preference on the server

Server-rendered pages and helpers can read the cookie-backed preference:

import { getTheme } from "@farm.js/core/theme/server";export default function SettingsPage() {  const theme = getTheme();  return <p>Saved preference: {theme}</p>;}

getTheme() returns the preference, not an invented server-side resolution for &quot;system&quot;; only the browser knows the visitor's operating-system color mode. Prefer CSS and useTheme for visual styling. When server-rendered content depends on the cookie, treat that route as request-specific rather than shared static output.

No-flash behavior

FARMJS places a small bootstrap script and color-scheme style at the start of the document head. They resolve the cookie and operating-system preference before application CSS loads. The runtime also follows operating-system changes while theme === &quot;system&quot; and preserves data-theme during SPA document navigation.

Configuration reference

Option Type Default Purpose
default &quot;light&quot; | &quot;dark&quot; | &quot;system&quot; &quot;system&quot; Preference used before a visitor chooses one.
storageKey string &quot;farm-theme&quot; Cookie and cross-tab storage key.