GlassKit

Theming & branding

Swap colors, fonts, and component styles across both apps. Tailwind v4 @theme tokens, the additive-display palette, the semantic-class pattern.

GlassKit uses Tailwind v4 with @theme tokens. Two surfaces, two stylesheets, one shared design language. Rebranding for your product takes ~10 minutes:

FileSurfaceOwns
companion/app/globals.cssNext.js companionCompanion-side palette (dark theme, accent green)
app/src/index.cssVite glasses appImports the library stylesheet + adds AI-response glue
packages/glasses-ui/styles.cssLibrary (shared)The additive-display palette + semantic classes (.screen, .focusable, .launcher-*)

The companion gets its own palette because it's a normal web surface: full color, gradients, all the usual. The glasses app inherits the additive-display palette from glasses-ui because the optics constrain it (pure black emits no light, so the design system has to be glowing marks on black).

The companion's tokens

Edit companion/app/globals.css:

companion/app/globals.css
@import "tailwindcss";

@theme {
  --color-bg: #08090b;
  --color-surface: #131418;
  --color-surface-2: #1c1e23;
  --color-ink: #f2f3f5;
  --color-ink-dim: #9aa0a8;
  --color-rule: #232629;
  --color-rule-hover: #34383d;
  --color-accent: #36e27f;          /* the green CTA color */
  --color-accent-ink: #04210f;      /* dark text on the accent bg */

  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}

Every --color-X token automatically becomes a Tailwind utility: bg-bg, text-ink-dim, border-rule, bg-accent text-accent-ink, etc. To rebrand:

Swap the accent hex. --color-accent is the primary CTA and brand color. Pick one with good contrast against the dark surface.

Pick a matching --color-accent-ink. This is the text color that sits ON the accent. Usually a darkened version of the accent (don't use pure black; accent backgrounds need a tinted foreground for harmony).

Swap the font. Replace the --font-sans value or add a --font-display for headings (the marketing site uses Bricolage_Grotesque via next/font/google).

That's the entire rebrand for the companion. No component edits required. Every component reads from the tokens.

The library's tokens (the glasses app)

Don't fork the library file

Override in your fork's app stylesheet by re-declaring the tokens AFTER the library import.

app/src/index.css
@import "tailwindcss";
@import "@glasskit/glasses-ui/styles.css";

/* Your override — wins because it's loaded later */
@theme {
  --color-accent: #ff7e6b;      /* your brand color */
  --color-room: #050507;        /* the dev-only "around-the-viewport" color */
}

The library's defaults:

packages/glasses-ui/styles.css
@theme {
  --color-bg: #000;             /* pure black — transparent on the device */
  --color-ink: #f2f3f5;
  --color-ink-dim: #9aa0a8;
  --color-accent: #36e27f;      /* phosphor green */
  --color-surface: #14171c;
  --color-rule: #2a2f37;
  --color-room: #07080a;        /* dark room around the viewport — local dev only */

  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}

Constraints from the optics

The Meta Ray-Ban Display is additive: it adds light to what the wearer sees through the lens. That has hard implications:

Optics-driven rules

  • --color-bg should stay #000. Any non-black background emits unintended light. Even very-dark gray (#0a0a0a) shows up as a faint glow on the optics.
  • --color-accent should be bright and saturated. Pastels and desaturated tones wash out. The default phosphor green (#36e27f) is calibrated for the display; replacement colors should be similarly bright (think CRT-style hex: #ff7e6b coral, #5ad8ff cyan, #ffce6b amber all work).
  • --color-ink matters less than you'd think. Most text on the glasses app is small enough that subtle gradations don't perceptibly differ. Off-white reads cleaner than pure white.
  • --color-room is dev-only: it's the area AROUND the 600×600 viewport in the browser preview. Doesn't render on the device; pick whatever color makes the local preview pleasant.

The semantic classes

glasses-ui/styles.css ships a set of @apply-based classes that every demo uses:

  • .glass-viewport / .glass-viewport--frame: the 600×600 container styling
  • .screen: flex column layout for a full screen
  • .focusable: base interactive styling + the focus ring
  • .launcher, .launcher-grid, .launcher-card, .launcher-card__label, .launcher-card__tagline: the demo launcher
  • .app-head, .row, .readout, .label, .dim, .cue-script, .cue-line, .cue-bar: the typography + layout classes the demos compose

These are semantic by intent: .screen doesn't mean "a flex column with gap: 12px"; it means "the page-level layout of a screen on the glasses." If you want a slightly different gap, you edit glasses-ui/styles.css once and every demo updates.

The classes are public API of the library. Override carefully in your fork.

Adding your own classes

In app/src/index.css, after the library import:

app/src/index.css
@import "tailwindcss";
@import "@glasskit/glasses-ui/styles.css";

.my-special-card {
  @apply rounded-md border border-rule bg-surface p-4;
}

.ai-response {
  /* Existing class — extend it */
  @apply m-0 min-h-[2.6em] w-full text-center leading-[1.5] text-ink;
  font-size: 13px;
}

Use Tailwind utilities (@apply) over custom CSS where possible. The utilities already reference your --color-* tokens, so a theme swap doesn't require touching individual classes.

Fonts

The companion uses next/font/google. Edit companion/app/(web)/layout.tsx (or whichever layout you're targeting):

companion/app/(web)/layout.tsx
import { Bricolage_Grotesque, Hanken_Grotesk } from "next/font/google";

const display = Bricolage_Grotesque({ variable: "--font-display", subsets: ["latin"], weight: ["400","600","700","800"] });
const body = Hanken_Grotesk({ variable: "--font-body", subsets: ["latin"], weight: ["400","500","600","700"] });

Then in globals.css:

companion/app/globals.css
@theme {
  --font-display: var(--font-display), ui-sans-serif, system-ui;
  --font-sans: var(--font-body), ui-sans-serif, system-ui;
}

next/font/google self-hosts the font files, so no CDN dependency or layout shift.

Same --font-sans token in glasses-ui/styles.css (override in app/src/index.css). The glasses app uses system fonts by default to avoid an extra HTTP request; the surface is small enough that a custom font barely shows. If you want a custom font for branding, load it via @font-face in index.css and assign to --font-sans.

Dark / light theme

Both apps ship dark-only by default. It's the right default for a glasses-adjacent product and matches the additive display constraint. If you want a light mode for the companion:

Move the dark tokens into @theme under a .dark selector via @variant, OR use Tailwind v4's prefers-color-scheme media query.

Define light tokens at the root @theme level.

Wire next-themes (already a dep) to toggle between them.

The glasses app stays dark always; additive display means there is no "light mode," only "less black."

Tokens you probably won't touch

  • --color-rule / --color-rule-hover: hairline dividers and card borders. Stay subtle.
  • --color-surface / --color-surface-2: card backgrounds. Each half-step lighter than --color-bg adds visual hierarchy.
  • --color-ink-dim: secondary text. Adjusting this changes the contrast between headlines and metadata.

Most rebrands change --color-accent + --font-* and call it done. The architecture lets you go deeper, but the design system is already calibrated, so fight it only if your brand actually demands it.

On this page