@alinaai/apps-sdk-theme
v0.1.1
Published
AlinaAI design tokens — CSS override layer for @openai/apps-sdk-ui
Readme
@alinaai/apps-sdk-theme
A CSS-only design token override layer for @openai/apps-sdk-ui. Rethemes all OpenAI Apps SDK components with AlinaAI branding — no JavaScript changes required.
Why This Exists
OpenAI's Apps SDK (@openai/apps-sdk-ui) provides a component library (Button, Image, Badge, Alert, etc.) styled with OpenAI's design tokens. These tokens are CSS custom properties organized in three tiers:
- Primitives — raw color palettes (gray, blue, green, red, etc.)
- Semantic — maps colors to purpose (primary solid, danger text, success background, etc.)
- Components — per-component adjustments (button font weight, badge radius, etc.)
All three tiers are declared in @layer theme using :root and [data-theme] selectors. This means any CSS imported after the OpenAI base can override them via the standard cascade — last declaration in the same layer wins.
@alinaai/apps-sdk-theme exploits this by providing a complete set of overrides for all three tiers. The result: every OpenAI component renders with AlinaAI visual styling, without touching any JavaScript, component props, or import paths.
How It Works
The package contains three CSS files imported in order:
index.css
├── primitives.css (Tier 1: color palettes)
├── semantic.css (Tier 2: semantic mappings)
└── components.css (Tier 3: component adjustments)Each file uses the exact same @layer theme declaration and selector patterns as OpenAI:
@layer theme {
:root, :where([data-theme]) { /* shared tokens */ }
:where(:root), :where([data-theme="light"]) { /* light mode */ }
:where([data-theme="dark"]) { /* dark mode */ }
}Because our @import comes after @openai/apps-sdk-ui/css, our values take precedence.
Token Resolution Chain
When a component like <Button color="primary" variant="solid"> renders:
Button.module.css
→ --button-background-color: var(--color-background-primary-solid)
semantic.css (AlinaAI override)
→ --color-background-primary-solid: var(--blue-600)
primitives.css (AlinaAI override)
→ --blue-600: #0074B8
Result: Button background = #0074B8 (AlinaAI Blue)Without the AlinaAI theme, the same chain resolves to OpenAI's --gray-900 (#181818) — a near-black button.
Usage
In an OpenAI Apps SDK app
Add one CSS import after the OpenAI base:
@import "tailwindcss";
@import "@openai/apps-sdk-ui/css";
@import "@alinaai/apps-sdk-theme";That's it. Every component in the app renders with AlinaAI styling.
As a local dependency
If the package isn't published to npm, reference it as a local path in package.json:
{
"dependencies": {
"@alinaai/apps-sdk-theme": "file:./alinaai-apps-sdk-theme"
}
}As an npm package
npm install @alinaai/apps-sdk-themeWhat Changes
Tier 1: Primitives
All color palettes are replaced with AlinaAI brand values:
| Palette | OpenAI Example | AlinaAI Value | Notes |
|---------|---------------|-------------|-------|
| Gray 900 | #181818 | #161B1F | Warmer neutral |
| Gray 1000 | #0d0d0d | #010B13 | Deep navy-black |
| Blue 400 | #0285ff | #04A4E9 | Brighter, more cyan |
| Blue 600 | #004f99 | #0074B8 | Primary brand color |
| Green 400 | #04b84c | #00CC62 | Brighter green |
| Red 500 | #e02e2a | #E12119 | Slightly shifted |
| Orange 400 | #fb6a22 | #F26B1D | Accent color |
| Purple → Violet | #924ff7 | #A77EF0 | Mapped to violet palette |
| Pink → Magenta | #ff66ad | #E561C8 | Mapped to magenta palette |
Alpha transparency values (--alpha-*) are NOT overridden — they're computed from --alpha-base which we do override per light/dark mode.
Tier 2: Semantic
The key brand change:
| Token | OpenAI | AlinaAI |
|-------|--------|-------|
| --color-background-primary-solid | var(--gray-900) (dark) | var(--blue-600) (AlinaAI Blue) |
| --color-background-primary-solid-hover | var(--gray-700) | var(--blue-700) |
All semantic state colors (info, warning, caution, danger, success, discovery) are remapped to use AlinaAI's primitive palette. Both light and dark mode are fully covered.
Tier 3: Components
| Token | OpenAI | AlinaAI | Reason |
|-------|--------|-------|--------|
| --button-font-weight | 500 (medium) | 600 (semibold) | Bolder CTAs |
| --badge-radius-* | var(--radius-xs) | var(--radius-full) | Pill-shaped badges |
| --switch-track-color-checked | var(--gray-900) | var(--blue-600) | Brand color toggle |
| --slider-range-color | var(--gray-450) | var(--blue-500) | Brand color slider |
| --link-primary-text-color | var(--blue-500) | var(--blue-600) | AlinaAI link blue |
What Does NOT Change
- Component JavaScript (Button, Image, Badge, etc.)
- Component props API (
color,variant,size) - Hooks (
useWidgetProps,useWidgetState,useOpenAiGlobal) - Tailwind utility classes in JSX
- MCP server code
- Widget HTML structure
- Import paths (still
@openai/apps-sdk-ui/components/Button)
File Structure
alinaai-apps-sdk-theme/
├── package.json # npm package metadata
├── index.css # Entry point — imports all three tiers
├── primitives.css # Tier 1: raw color palette overrides
├── semantic.css # Tier 2: semantic token overrides (light + dark)
├── components.css # Tier 3: component-specific adjustments
└── README.md # This fileCustomizing Further
Developers can add their own overrides after the AlinaAI theme:
@import "tailwindcss";
@import "@openai/apps-sdk-ui/css";
@import "@alinaai/apps-sdk-theme";
/* Your custom overrides — these win over AlinaAI */
@layer theme {
:root {
--color-background-primary-solid: #e63946;
}
}Design Token Architecture Reference
The full token hierarchy:
Tier 1: Primitives (raw values)
--blue-600: #0074B8
↓
Tier 2: Semantic (purpose mapping)
--color-background-primary-solid: var(--blue-600)
↓
Tier 3: Components (component-specific)
--button-font-weight: var(--font-weight-semibold)
↓
Component CSS Module (Button.module.css)
--button-background-color: var(--color-background-primary-solid)
background-color: var(--button-background-color)
↓
Rendered: blue button with semibold textCompatibility
- Requires
@openai/apps-sdk-ui>= 0.2.0 - Works with Tailwind CSS v4
- Supports light and dark mode via
[data-theme]attribute - No JavaScript runtime dependency
