
Phoenix Design
- 8 installs
- 10.9k repo stars
- Updated August 4, 2026
- arize-ai/phoenix
phoenix-design is a Claude Code skill defining the Phoenix frontend design system: layout, dialogs, error display, BEM CSS naming, and CSS design-token conventions.
About
phoenix-design documents the design-system conventions for the Phoenix frontend, covering layout, dialogs, error display, BEM CSS class naming, and CSS design tokens. Developers use it when building UI, naming CSS classes, creating or consuming tokens, or designing dialog interactions in the app/src directory. It routes to per-task rule files and uses RFC 2119 keywords to state requirements.
- Design-system conventions for the Phoenix frontend (layout, dialogs, errors)
- BEM CSS class naming rules and CSS design-token conventions
- RFC 2119 keyword-driven rules with per-task rule files
Phoenix Design by the numbers
- 8 all-time installs (skills.sh)
- +4 installs in the week ending Jul 12, 2026 (Skillselion tracking)
- Ranked #1,483 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
phoenix-design capabilities & compatibility
- Capabilities
- design system rules · css naming · design tokens · error display patterns
- Use cases
- ui design · frontend
- Runs
- Runs locally
- Pricing
- Free
What phoenix-design says it does
Design system conventions for the Phoenix frontend — layout, dialogs, error display, BEM CSS class naming, and CSS design tokens.
| `rules/bem.md` | Naming CSS classes |
| `rules/tokens.md` | Creating or consuming CSS design tokens |
npx skills add https://github.com/arize-ai/phoenix --skill phoenix-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 10.9k |
| Last updated | August 4, 2026 |
| Repository | arize-ai/phoenix ↗ |
What it does
Follow Phoenix's design-system rules for layout, dialogs, error display, BEM class naming, tokens, and icon choice when building UI in app/src.
Who is it for?
Developers building or styling UI inside the Phoenix app/src frontend
Skip if: Projects outside the Phoenix repo, since the rules are Phoenix-specific conventions
When should I use this skill?
You are building UI, naming CSS classes, creating or consuming tokens, or designing dialogs in app/src
What you get
UI code that follows Phoenix's layout stability, dialog, error-display, BEM, and token rules
- BEM-named CSS classes
- consistent dialog and error-display patterns
- CSS design tokens
By the numbers
- 6 rule files: layout, dialogs, error-display, bem, tokens, icons
Files
Phoenix Design System
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this specification are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
Rule Files
Read the relevant file(s) based on the task:
| Rule file | When to read |
|---|---|
rules/layout.md | Layout stability, scroll behavior, interaction patterns |
rules/dialogs.md | Alert dialog usage, variants, and content writing |
rules/error-display.md | Error scoping, inline alerts, input validation |
rules/bem.md | Naming CSS classes |
rules/tokens.md | Creating or consuming CSS design tokens |
rules/icons.md | Picking an icon for a noun (project, trace, span, file, etc.) |
BEM CSS Class Naming
Pattern
block → disclosure, tabs, slider
block__element → slider__label, dialog__title, search-field__icon
block--modifier → theme--dark, dropdown--pickerRules
- Block: A standalone component —
disclosure,search-field,toggle-button - Element: A part that cannot exist independently — uses
__separator —slider__label,field__icon - Modifier: A variant or state — uses
--separator —theme--dark,dropdown--picker - Compound names within a segment MUST use hyphens:
search-field,toggle-button,dialog__close-button - Class names MUST NOT have any prefix
Examples
className="search-field"
className="search-field__clear"
className="disclosure__panel"
className="theme--dark"Alert Dialogs
Alert dialogs are interruptive — they block all other interaction until resolved. They MUST only be used when a user must acknowledge important information or confirm a consequential action before proceeding.
When to use
- Confirming a destructive or irreversible action (delete project, clear traces, remove data)
- Communicating a critical error that blocks the workflow
- Sharing time-sensitive warnings the user must consider
Alert dialogs MUST NOT be used for success messages, low-signal notifications, or excessive confirmations.
One at a time
Alert dialogs MUST NOT be nested. Only one SHOULD be displayed at any moment. If a flow seems to require sequential decisions, redesign the interaction.
Variants
| Variant | Goal | Tone |
|---|---|---|
| Confirmation | Ask a user to confirm an action they initiated | Instructive |
| Information | Share important info a user must acknowledge | Helpful |
| Warning | Share time-sensitive info that won't block proceeding | Instructive to helpful |
| Destructive | Warn that proceeding may negatively impact data | Instructive |
| Error | Communicate a critical issue that must be resolved before continuing | Supportive |
Writing the title
- Every alert dialog MUST have a title.
- The title communicates the outcome or effect — not that something "went wrong."
- The title SHOULD use the same or similar phrasing as the action that triggered the dialog (e.g., action "Delete project" → title "Delete project").
- Titles SHOULD be as close to a complete sentence as possible (subject + verb). No end punctuation.
- Titles MUST NOT ask questions ("Are you sure you want to delete this project?"). Reframe as the outcome ("Delete project").
Title examples
| Context | Good | Bad |
|---|---|---|
| Confirmation | "Delete 3 experiments" | "Are you sure?" |
| Error | "Failed to load traces" | "An error occurred" |
| Destructive | "Clear project data" | "Do you want to clear?" |
Writing the description
- Descriptions MUST provide the additional context a user needs to make a decision.
- Descriptions MUST be written in complete sentences.
- Error codes SHOULD be included in parentheses at the end of the last sentence if applicable.
Description examples
| Context | Example |
|---|---|
| Delete dataset | "This will also delete all associated experiments and traces, and it cannot be undone." |
| Delete API key | "This cannot be undone and will disable all uses of this key." |
| Delete prompt | "This action cannot be undone and all services dependent on this prompt will be affected." |
Writing the actions
- Button labels MUST be specific and actionable — a user SHOULD be able to understand the dialog's message from the button label alone.
- Labels SHOULD mirror the language from the title when possible (title "Delete experiment" → primary action "Delete experiment").
- Labels MUST NOT use generic words like "Yes" or "No." Use labels that describe what happens ("Delete", "Clear", "Remove data").
- Confirmation SHOULD be paired with distinct actions that give the user control.
Action examples
| Good | Bad |
|---|---|
| Delete experiment | Yes |
| Clear project | OK |
| Remove data | No |
| Cancel | Dismiss |
Error Display
Never use toasts for errors
Error toasts (useNotifyError) are an accessibility anti-pattern — they are ephemeral and may disappear before assistive technology users can read them. Toasts MUST NOT be used to display errors.
Use inline <Alert variant="danger"> banners instead. Error messages SHOULD be stored in local component state with useState<string | null>(null).
Error scoping
Errors MUST be scoped to the appropriate level. Inline errors using error slots and existing field patterns SHOULD be preferred. Alert banners SHOULD be used for broader errors.
| Scope | Display |
|---|---|
| Element (field invalid) | error slot |
| Section (group invalid) | Section-level alert |
Pattern
const [error, setError] = useState<string | null>(null);
// In mutation onError:
onError: (error) => {
setError(error.message);
},
// In JSX — render at the top of the dialog/form, after the header:
{error && <Alert variant="danger">{error}</Alert>}Dialog placement
In dialogs/modals, the error Alert MUST be placed after the DialogHeader and before the body content. Use the banner prop for full-width styling:
<DialogHeader>...</DialogHeader>
{error && (
<View paddingX="size-200" paddingTop="size-100">
<Alert variant="danger" banner>{error}</Alert>
</View>
)}
<View padding="size-200">
{/* dialog body */}
</View>Error state lifecycle
- Clear on reopen: Error state MUST be reset when a dialog reopens so stale errors don't persist.
- Clear on resubmit: Error state MUST be reset at the start of a new submission attempt.
- Keep errors local: Error state MUST belong to the component that owns the mutation — errors MUST NOT be propagated up to parent components via callback props.
When toasts ARE appropriate
Toasts MAY be used for success notifications (useNotifySuccess) since those are informational and non-critical if missed. Only errors require persistent, inline display.
Input validation
Input restrictions MUST be communicated via description slots before submission — do not wait for a submit attempt to tell the user what's required.
Icons — Noun → Icon Mapping
Phoenix has a curated icon set in app/src/components/core/icon/Icons.tsx. Use this canonical mapping so that the same Phoenix concept (the noun) is always represented by the same glyph across the app. Consistency matters more than aesthetic preference — when the noun appears, reach for the icon below.
Canonical mapping
| Noun | Icon | Notes |
|---|---|---|
| Project | Icons.GridOutline | Phoenix projects (the namespace concept). |
| Trace | Icons.Trace | A single trace. |
| Span | Icons.WorkflowOutline | A single span. The Lucide-style workflow glyph (two connected nodes) reads as a step in a workflow, which matches how spans relate. |
| Span filter | Icons.FunnelOutline | A validated span filter expression. |
| Source / citation | Icons.BookOutline | Source documents surfaced from RAG / search. |
| Document (generic) | Icons.FileTextOutline | Text-bearing documents (PDF, txt, md). |
| File (generic / unknown) | Icons.FileOutline | Default file fallback when the media type is not specifically handled. |
| Image | Icons.ImageOutline | Image-typed file attachments. |
| Video | Icons.PlayCircleOutline | Video-typed file attachments (no dedicated Video* icon). |
| Context (generic) | Icons.InfoOutline | Default for an AttachmentContextData whose category has no canonical icon yet. |
When you need an icon
1. Look up the noun above. If it is listed, use that icon. 2. If it is not listed but the icon exists in Icons.tsx, add a row to the table above in the same PR so the next person finds it. 3. If neither the noun nor the icon exists, add the icon to Icons.tsx (see "Adding a new icon" below) and add the row to the table.
Adding a new icon
Icons.tsx is alphabetically sectioned (//A, //B, …). Two coexisting style families:
- Eva-icons style (the majority) — filled paths, no
strokeattribute,viewBox="0 0 24 24". Render at the icon container'sfont-size. - Lucide style —
fill="none",stroke="currentColor", explicitwidth="20" height="20". When porting a Lucide SVG: - Convert kebab-case attributes to camelCase (
stroke-width→strokeWidth, etc.). - Set
fill="none"on the root and on each child shape — JSX/React rendering does not always honor inheritance the same way as raw SVG. - Match the
GitBranchOutline/WorkflowOutlineshape exactly so future Lucide additions stay consistent.
Export as <Name>Outline (or <Name>Filled) to match the existing naming convention. After adding, update the table in this file in the same change.
Layout & Interaction
Layout stability
Loading states MUST use skeleton loaders so the page does not shift between loading and loaded states.
Scroll behavior
Pages SHOULD have only one scrollable region. Scroll traps where the user's scroll gets captured by a nested container MUST be avoided.
Interaction patterns
Each action MUST have one method of invocation. Confirm/cancel SHOULD be placed at the bottom of a dialog — controls MUST NOT be duplicated across header, footer, and shortcuts. Exception: dialog close buttons MAY appear in both the top-right corner and the footer.
Row actions (e.g. delete, edit) for simple list or menu items MUST be always visible. Hiding actions behind hover states reduces discoverability and is inaccessible to touch users.
Design Token Naming Guide
What are design tokens
Design tokens are design decisions translated into CSS custom properties. They provide a single source of truth for colors, sizing, typography, borders, and spacing.
- Defined in
app/src/GlobalStyles.tsx - Consumed via
var(--token-name)in Emotion CSS-in-JS - Theme-aware: light and dark themes override token values
Token types
| Type | Prefix/Pattern | Example | Purpose |
|---|---|---|---|
| Dimension (scale) | --global-dimension-size-{n} | --global-dimension-size-200 → 16px | Responsive sizing on 8px grid |
| Dimension (static) | --global-dimension-static-size-{n} | --global-dimension-static-size-100 → 8px | Fixed pixel values |
| Font size (scale) | --global-dimension-font-size-{n} | --global-dimension-font-size-100 → 14px | Numeric scale font sizes |
| Font size (named) | --global-font-size-{t-shirt} | --global-font-size-s → 14px | T-shirt sized fonts (xxs–xxl) |
| Line height | --global-line-height-{t-shirt} | --global-line-height-s → 20px | T-shirt sized line heights |
| Global color | --global-color-{hue}-{intensity} | --global-color-blue-500 | Raw color palette (100–1400) |
| Semantic color | --global-color-{intent} | --global-color-danger | Purpose: danger, success, warning, severe, info |
| Primary color | --global-color-primary-{opacity} | --global-color-primary-500 | Gray-900 at opacity levels |
| Text color | --global-text-color-{opacity} | --global-text-color-700 | Text at 90%/70%/50%/30% opacity |
| Static color | --global-static-color-{bw}-{opacity} | --global-static-color-white-900 | Theme-invariant white/black |
| Border | --global-border-size-{name} / --global-border-color-{variant} | --global-border-size-thin | Widths and semantic border colors |
| Rounding | --global-rounding-{name} | --global-rounding-medium | Border radius (xsmall–full) |
| Grid/Layout | --global-grid-{property}-{size} | --global-grid-gutter-small | Grid gutters, margins, baseline |
| Component-specific | --global-{component}-{property} | --global-button-primary-background-color | Per-component design decisions |
| Chart | --chart-{element}-{property} | --chart-axis-stroke-color | Chart visualization tokens |
| CodeMirror | --code-mirror-{element}-{property} | --code-mirror-editor-background-color | Editor tokens |
Naming structure
3-part structure following the context → common unit → clarification pattern:
--{scope}-{category}-{subcategory}-{variant}-{state}- scope:
globalfor system tokens; component/domain name for scoped tokens (chart,code-mirror) - category: The token family —
color,dimension,font-size,border,rounding,button,table, etc. - subcategory: The specific property —
background-color,border-color,foreground-color,size,height - variant: Scale value (100–1400), t-shirt size (s/m/l), semantic name (primary/danger/success)
- state: Interaction state when needed —
hover,active,pressed,disabled
Decomposed examples
--global-button-primary-background-color-hover→ global + button + primary + background-color + hover--global-color-gray-500→ global + color + gray + 500--global-rounding-medium→ global + rounding + medium--global-table-row-border-color→ global + table + row + border-color--chart-cartesian-grid-stroke-color→ chart + cartesian-grid + stroke-color
Alias tokens
Tokens that reference other tokens rather than hard-coded values. This is how Phoenix connects raw values to semantic meaning:
--global-color-primary: var(--global-color-gray-900);
--global-border-color-default: var(--global-color-gray-300);
--global-rounding-small: var(--global-dimension-static-size-50);
--global-button-primary-bg: var(--global-color-gray-900);RGB companion pattern
Global colors provide -rgb variants for alpha manipulation:
--global-color-gray-500-rgb: 141, 141, 141;
--global-color-gray-500: rgb(var(--global-color-gray-500-rgb));
/* Usage with custom alpha: */
background: rgba(var(--global-color-gray-500-rgb), 0.3);Usage guidelines
1. Prefer alias/semantic tokens over globals — use --global-color-danger not --global-color-red-700 2. Prefer named sizes over numeric — use --global-font-size-s not --global-dimension-font-size-100 3. Use component tokens for their component — --global-button-* for buttons only 4. Never hardcode values that have token equivalents 5. Use RGB variants for alpha needs — rgba(var(--global-color-gray-900-rgb), 0.5)
Adding new tokens
- Global tokens → add to the appropriate CSS block in
GlobalStyles.tsx(baseTokensCSS,borderAndGridCSS, etc.) - Component tokens → create a
{component}CSS(theme: Theme)function, add toderivedCSS()composition - Theme-dependent tokens → must be defined per theme or use existing theme-aware aliases
- Values should reference other tokens where possible (aliasing over hardcoding)