coolify/.ai/design-system.md
Andras Bacsai 425eff0a58 docs: add Coolify design system reference
Add comprehensive AI/LLM-consumable design system documentation covering:
- Design tokens (colors, typography, spacing, shadows, focus rings)
- Dark mode strategy with accent color swaps and background hierarchy
- Component catalog (buttons, inputs, selects, cards, navigation, modals, etc.)
- Interactive state reference (focus, hover, disabled, readonly)
- CSS custom properties for theme tokens

Includes both Tailwind CSS classes and plain CSS equivalents for all components.
2026-02-01 19:02:31 +01:00

1666 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Coolify Design System
> **Purpose**: AI/LLM-consumable reference for replicating Coolify's visual design in new applications. Contains design tokens, component styles, and interactive states — with both Tailwind CSS classes and plain CSS equivalents.
---
## 1. Design Tokens
### 1.1 Colors
#### Brand / Accent
| Token | Hex | Usage |
|---|---|---|
| `coollabs` | `#6b16ed` | Primary accent (light mode) |
| `coollabs-50` | `#f5f0ff` | Highlighted button bg (light) |
| `coollabs-100` | `#7317ff` | Highlighted button hover (dark) |
| `coollabs-200` | `#5a12c7` | Highlighted button text (light) |
| `coollabs-300` | `#4a0fa3` | Deepest brand shade |
| `warning` / `warning-400` | `#fcd452` | Primary accent (dark mode) |
#### Warning Scale (used for dark-mode accent + callouts)
| Token | Hex |
|---|---|
| `warning-50` | `#fefce8` |
| `warning-100` | `#fef9c3` |
| `warning-200` | `#fef08a` |
| `warning-300` | `#fde047` |
| `warning-400` | `#fcd452` |
| `warning-500` | `#facc15` |
| `warning-600` | `#ca8a04` |
| `warning-700` | `#a16207` |
| `warning-800` | `#854d0e` |
| `warning-900` | `#713f12` |
#### Neutral Grays (dark mode backgrounds)
| Token | Hex | Usage |
|---|---|---|
| `base` | `#101010` | Page background (dark) |
| `coolgray-100` | `#181818` | Component background (dark) |
| `coolgray-200` | `#202020` | Elevated surface / borders (dark) |
| `coolgray-300` | `#242424` | Input border shadow / hover (dark) |
| `coolgray-400` | `#282828` | Tooltip background (dark) |
| `coolgray-500` | `#323232` | Subtle hover overlays (dark) |
#### Semantic
| Token | Hex | Usage |
|---|---|---|
| `success` | `#22C55E` | Running status, success alerts |
| `error` | `#dc2626` | Stopped status, danger actions, error alerts |
#### Light Mode Defaults
| Element | Color |
|---|---|
| Page background | `gray-50` (`#f9fafb`) |
| Component background | `white` (`#ffffff`) |
| Borders | `neutral-200` (`#e5e5e5`) |
| Primary text | `black` (`#000000`) |
| Muted text | `neutral-500` (`#737373`) |
| Placeholder text | `neutral-300` (`#d4d4d4`) |
### 1.2 Typography
**Font family**: Inter, sans-serif (weights 100900, woff2, `font-display: swap`)
#### Heading Hierarchy
> **CRITICAL**: All headings and titles (h1h4, card titles, modal titles) MUST be `white` (`#fff`) in dark mode. The default body text color is `neutral-400` (`#a3a3a3`) — headings must override this to white or they will be nearly invisible on dark backgrounds.
| Element | Tailwind | Plain CSS (light) | Plain CSS (dark) |
|---|---|---|---|
| `h1` | `text-3xl font-bold dark:text-white` | `font-size: 1.875rem; font-weight: 700; color: #000;` | `color: #fff;` |
| `h2` | `text-xl font-bold dark:text-white` | `font-size: 1.25rem; font-weight: 700; color: #000;` | `color: #fff;` |
| `h3` | `text-lg font-bold dark:text-white` | `font-size: 1.125rem; font-weight: 700; color: #000;` | `color: #fff;` |
| `h4` | `text-base font-bold dark:text-white` | `font-size: 1rem; font-weight: 700; color: #000;` | `color: #fff;` |
#### Body Text
| Context | Tailwind | Plain CSS |
|---|---|---|
| Body default | `text-sm antialiased` | `font-size: 0.875rem; line-height: 1.25rem; -webkit-font-smoothing: antialiased;` |
| Labels | `text-sm font-medium` | `font-size: 0.875rem; font-weight: 500;` |
| Badge/status text | `text-xs font-bold` | `font-size: 0.75rem; line-height: 1rem; font-weight: 700;` |
| Box description | `text-xs font-bold text-neutral-500` | `font-size: 0.75rem; font-weight: 700; color: #737373;` |
### 1.3 Spacing Patterns
| Context | Value | CSS |
|---|---|---|
| Component internal padding | `p-2` | `padding: 0.5rem;` |
| Callout padding | `p-4` | `padding: 1rem;` |
| Input vertical padding | `py-1.5` | `padding-top: 0.375rem; padding-bottom: 0.375rem;` |
| Button height | `h-8` | `height: 2rem;` |
| Button horizontal padding | `px-2` | `padding-left: 0.5rem; padding-right: 0.5rem;` |
| Button gap | `gap-2` | `gap: 0.5rem;` |
| Menu item padding | `px-2 py-1` | `padding: 0.25rem 0.5rem;` |
| Menu item gap | `gap-3` | `gap: 0.75rem;` |
| Section margin | `mb-12` | `margin-bottom: 3rem;` |
| Card min-height | `min-h-[4rem]` | `min-height: 4rem;` |
### 1.4 Border Radius
| Context | Tailwind | Plain CSS |
|---|---|---|
| Default (inputs, buttons, cards, modals) | `rounded-sm` | `border-radius: 0.125rem;` |
| Callouts | `rounded-lg` | `border-radius: 0.5rem;` |
| Badges | `rounded-full` | `border-radius: 9999px;` |
| Cards (coolbox variant) | `rounded` | `border-radius: 0.25rem;` |
### 1.5 Shadows
#### Input / Select Box-Shadow System
Coolify uses **inset box-shadows instead of borders** for inputs and selects. This enables a unique "dirty indicator" — a colored left-edge bar.
```css
/* Default state */
box-shadow: inset 4px 0 0 transparent, inset 0 0 0 2px #e5e5e5;
/* Default state (dark) */
box-shadow: inset 4px 0 0 transparent, inset 0 0 0 2px #242424;
/* Focus state (light) — purple left bar */
box-shadow: inset 4px 0 0 #6b16ed, inset 0 0 0 2px #e5e5e5;
/* Focus state (dark) — yellow left bar */
box-shadow: inset 4px 0 0 #fcd452, inset 0 0 0 2px #242424;
/* Dirty (modified) state — same as focus */
box-shadow: inset 4px 0 0 #6b16ed, inset 0 0 0 2px #e5e5e5; /* light */
box-shadow: inset 4px 0 0 #fcd452, inset 0 0 0 2px #242424; /* dark */
/* Disabled / Readonly */
box-shadow: none;
```
#### Input-Sticky Variant (thinner border)
```css
/* Uses 1px border instead of 2px */
box-shadow: inset 4px 0 0 transparent, inset 0 0 0 1px #e5e5e5;
```
### 1.6 Focus Ring System
All interactive elements (buttons, links, checkboxes) share this focus pattern:
**Tailwind:**
```
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base
```
**Plain CSS:**
```css
:focus-visible {
outline: none;
box-shadow: 0 0 0 2px #101010, 0 0 0 4px #6b16ed; /* light */
}
/* dark mode */
.dark :focus-visible {
box-shadow: 0 0 0 2px #101010, 0 0 0 4px #fcd452;
}
```
> **Note**: Inputs use the inset box-shadow system (section 1.5) instead of the ring system.
---
## 2. Dark Mode Strategy
- **Toggle method**: Class-based — `.dark` class on `<html>` element
- **CSS variant**: `@custom-variant dark (&:where(.dark, .dark *));`
- **Default border override**: All elements default to `border-color: var(--color-coolgray-200)` (`#202020`) instead of `currentcolor`
### Accent Color Swap
| Context | Light | Dark |
|---|---|---|
| Primary accent | `coollabs` (`#6b16ed`) | `warning` (`#fcd452`) |
| Focus ring | `ring-coollabs` | `ring-warning` |
| Input focus bar | `#6b16ed` (purple) | `#fcd452` (yellow) |
| Active nav text | `text-black` | `text-warning` |
| Helper/highlight text | `text-coollabs` | `text-warning` |
| Loading spinner | `text-coollabs` | `text-warning` |
| Scrollbar thumb | `coollabs-100` | `coollabs-100` |
### Background Hierarchy (dark)
```
#101010 (base) — page background
└─ #181818 (coolgray-100) — cards, inputs, components
└─ #202020 (coolgray-200) — elevated surfaces, borders, nav active
└─ #242424 (coolgray-300) — input borders (via box-shadow), button borders
└─ #282828 (coolgray-400) — tooltips, hover states
└─ #323232 (coolgray-500) — subtle overlays
```
### Background Hierarchy (light)
```
#f9fafb (gray-50) — page background
└─ #ffffff (white) — cards, inputs, components
└─ #e5e5e5 (neutral-200) — borders
└─ #f5f5f5 (neutral-100) — hover backgrounds
└─ #d4d4d4 (neutral-300) — deeper hover, nav active
```
---
## 3. Component Catalog
### 3.1 Button
#### Default
**Tailwind:**
```
flex gap-2 justify-center items-center px-2 h-8 text-sm text-black normal-case rounded-sm
border-2 outline-0 cursor-pointer font-medium bg-white border-neutral-200 hover:bg-neutral-100
dark:bg-coolgray-100 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-200
dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit
dark:disabled:text-neutral-600 disabled:border-transparent disabled:hover:bg-transparent
disabled:bg-transparent disabled:text-neutral-300
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs
dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base
```
**Plain CSS:**
```css
.button {
display: flex;
gap: 0.5rem;
justify-content: center;
align-items: center;
padding: 0 0.5rem;
height: 2rem;
font-size: 0.875rem;
font-weight: 500;
text-transform: none;
color: #000;
background: #fff;
border: 2px solid #e5e5e5;
border-radius: 0.125rem;
outline: 0;
cursor: pointer;
min-width: fit-content;
}
.button:hover { background: #f5f5f5; }
/* Dark */
.dark .button {
background: #181818;
color: #fff;
border-color: #242424;
}
.dark .button:hover {
background: #202020;
color: #fff;
}
/* Disabled */
.button:disabled {
cursor: not-allowed;
border-color: transparent;
background: transparent;
color: #d4d4d4;
}
.dark .button:disabled { color: #525252; }
```
#### Highlighted (Primary Action)
**Tailwind** (via `isHighlighted` attribute):
```
text-coollabs-200 dark:text-white bg-coollabs-50 dark:bg-coollabs/20
border-coollabs dark:border-coollabs-100 hover:bg-coollabs hover:text-white
dark:hover:bg-coollabs-100 dark:hover:text-white
```
**Plain CSS:**
```css
.button-highlighted {
color: #5a12c7;
background: #f5f0ff;
border-color: #6b16ed;
}
.button-highlighted:hover {
background: #6b16ed;
color: #fff;
}
.dark .button-highlighted {
color: #fff;
background: rgba(107, 22, 237, 0.2);
border-color: #7317ff;
}
.dark .button-highlighted:hover {
background: #7317ff;
color: #fff;
}
```
#### Error / Danger
**Tailwind** (via `isError` attribute):
```
text-red-800 dark:text-red-300 bg-red-50 dark:bg-red-900/30
border-red-300 dark:border-red-800 hover:bg-red-300 hover:text-white
dark:hover:bg-red-800 dark:hover:text-white
```
**Plain CSS:**
```css
.button-error {
color: #991b1b;
background: #fef2f2;
border-color: #fca5a5;
}
.button-error:hover {
background: #fca5a5;
color: #fff;
}
.dark .button-error {
color: #fca5a5;
background: rgba(127, 29, 29, 0.3);
border-color: #991b1b;
}
.dark .button-error:hover {
background: #991b1b;
color: #fff;
}
```
#### Loading Indicator
Buttons automatically show a spinner (SVG with `animate-spin`) next to their content during async operations. The spinner uses the accent color (`text-coollabs` / `text-warning`).
---
### 3.2 Input
**Tailwind:**
```
block py-1.5 w-full text-sm text-black rounded-sm border-0
dark:bg-coolgray-100 dark:text-white
disabled:bg-neutral-200 disabled:text-neutral-500 dark:disabled:bg-coolgray-100/40
dark:read-only:text-neutral-500 dark:read-only:bg-coolgray-100/40
placeholder:text-neutral-300 dark:placeholder:text-neutral-700
read-only:text-neutral-500 read-only:bg-neutral-200
focus-visible:outline-none
```
**Plain CSS:**
```css
.input {
display: block;
padding: 0.375rem 0.5rem;
width: 100%;
font-size: 0.875rem;
color: #000;
background: #fff;
border: 0;
border-radius: 0.125rem;
box-shadow: inset 4px 0 0 transparent, inset 0 0 0 2px #e5e5e5;
}
.input:focus-visible {
outline: none;
box-shadow: inset 4px 0 0 #6b16ed, inset 0 0 0 2px #e5e5e5;
}
.input::placeholder { color: #d4d4d4; }
.input:disabled { background: #e5e5e5; color: #737373; box-shadow: none; }
.input:read-only { color: #737373; background: #e5e5e5; box-shadow: none; }
.input[type="password"] { padding-right: 2.4rem; }
/* Dark */
.dark .input {
background: #181818;
color: #fff;
box-shadow: inset 4px 0 0 transparent, inset 0 0 0 2px #242424;
}
.dark .input:focus-visible {
box-shadow: inset 4px 0 0 #fcd452, inset 0 0 0 2px #242424;
}
.dark .input::placeholder { color: #404040; }
.dark .input:disabled { background: rgba(24, 24, 24, 0.4); box-shadow: none; }
.dark .input:read-only { color: #737373; background: rgba(24, 24, 24, 0.4); box-shadow: none; }
```
#### Dirty (Modified) State
When an input value has been changed but not saved, a 4px colored left bar appears via box-shadow — same colors as focus state. This provides a visual indicator that the field has unsaved changes.
---
### 3.3 Select
Same base styles as Input, plus a custom dropdown arrow SVG:
**Tailwind:**
```
w-full block py-1.5 text-sm text-black rounded-sm border-0
dark:bg-coolgray-100 dark:text-white
disabled:bg-neutral-200 disabled:text-neutral-500 dark:disabled:bg-coolgray-100/40
focus-visible:outline-none
```
**Additional plain CSS for the dropdown arrow:**
```css
.select {
/* ...same as .input base... */
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23000000'%3e%3cpath stroke-linecap='round' stroke-linejoin='round' d='M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1rem 1rem;
padding-right: 2.5rem;
appearance: none;
}
/* Dark mode: white stroke arrow */
.dark .select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23ffffff'%3e%3cpath stroke-linecap='round' stroke-linejoin='round' d='M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9'/%3e%3c/svg%3e");
}
```
---
### 3.4 Checkbox
**Tailwind:**
```
dark:border-neutral-700 text-coolgray-400 dark:bg-coolgray-100 rounded-sm cursor-pointer
dark:disabled:bg-base dark:disabled:cursor-not-allowed
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs
dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base
```
**Container:**
```
flex flex-row items-center gap-4 pr-2 py-1 form-control min-w-fit
dark:hover:bg-coolgray-100 cursor-pointer
```
**Plain CSS:**
```css
.checkbox {
border-color: #404040;
color: #282828;
background: #181818;
border-radius: 0.125rem;
cursor: pointer;
}
.checkbox:focus-visible {
outline: none;
box-shadow: 0 0 0 2px #101010, 0 0 0 4px #fcd452;
}
.checkbox-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 0.25rem 0.5rem 0.25rem 0;
min-width: fit-content;
cursor: pointer;
}
.dark .checkbox-container:hover { background: #181818; }
```
---
### 3.5 Textarea
Uses `font-mono` for monospace text. Supports tab key insertion (2 spaces).
**Important**: Large/multiline textareas should NOT use the inset box-shadow left-border system from `.input`. Use a simple border instead:
**Tailwind:**
```
block w-full text-sm text-black rounded-sm border border-neutral-200
dark:bg-coolgray-100 dark:text-white dark:border-coolgray-300
font-mono focus-visible:outline-none focus-visible:ring-2
focus-visible:ring-coollabs dark:focus-visible:ring-warning
focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base
```
**Plain CSS:**
```css
.textarea {
display: block;
width: 100%;
font-size: 0.875rem;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
color: #000;
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 0.125rem;
}
.textarea:focus-visible {
outline: none;
box-shadow: 0 0 0 2px #fff, 0 0 0 4px #6b16ed;
}
.dark .textarea {
background: #181818;
color: #fff;
border-color: #242424;
}
.dark .textarea:focus-visible {
box-shadow: 0 0 0 2px #101010, 0 0 0 4px #fcd452;
}
```
> **Note**: The 4px inset left-border (dirty/focus indicator) is only for single-line inputs and selects, not textareas.
---
### 3.6 Box / Card
#### Standard Box
**Tailwind:**
```
relative flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem]
dark:bg-coolgray-100 shadow-sm bg-white border text-black dark:text-white hover:text-black
border-neutral-200 dark:border-coolgray-300 hover:bg-neutral-100
dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline rounded-sm
```
**Plain CSS:**
```css
.box {
position: relative;
display: flex;
flex-direction: column;
padding: 0.5rem;
min-height: 4rem;
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 0.125rem;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
color: #000;
cursor: pointer;
transition: background-color 150ms, color 150ms;
text-decoration: none;
}
.box:hover { background: #f5f5f5; color: #000; }
.dark .box {
background: #181818;
border-color: #242424;
color: #fff;
}
.dark .box:hover {
background: #7317ff;
color: #fff;
}
/* IMPORTANT: child text must also turn white/black on hover,
since description text (#737373) is invisible on purple bg */
.box:hover .box-title { color: #000; }
.box:hover .box-description { color: #000; }
.dark .box:hover .box-title { color: #fff; }
.dark .box:hover .box-description { color: #fff; }
/* Desktop: row layout */
@media (min-width: 1024px) {
.box { flex-direction: row; }
}
```
#### Coolbox (Ring Hover)
**Tailwind:**
```
relative flex transition-all duration-150 dark:bg-coolgray-100 bg-white p-2 rounded
border border-neutral-200 dark:border-coolgray-400 hover:ring-2
dark:hover:ring-warning hover:ring-coollabs cursor-pointer min-h-[4rem]
```
**Plain CSS:**
```css
.coolbox {
position: relative;
display: flex;
padding: 0.5rem;
min-height: 4rem;
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 0.25rem;
cursor: pointer;
transition: all 150ms;
}
.coolbox:hover { box-shadow: 0 0 0 2px #6b16ed; }
.dark .coolbox {
background: #181818;
border-color: #282828;
}
.dark .coolbox:hover { box-shadow: 0 0 0 2px #fcd452; }
```
#### Box Text
> **IMPORTANT — Dark mode titles**: Card/box titles MUST be `#fff` (white) in dark mode, not the default body text color (`#a3a3a3` / neutral-400). A black or grey title is nearly invisible on dark backgrounds (`#181818`). This applies to all heading-level text inside cards.
```css
.box-title {
font-weight: 700;
color: #000; /* light mode: black */
}
.dark .box-title {
color: #fff; /* dark mode: MUST be white, not grey */
}
.box-description {
font-size: 0.75rem;
font-weight: 700;
color: #737373;
}
/* On hover: description must become visible against colored bg */
.box:hover .box-description { color: #000; }
.dark .box:hover .box-description { color: #fff; }
```
---
### 3.7 Badge / Status Indicator
**Tailwind:**
```
inline-block w-3 h-3 text-xs font-bold rounded-full leading-none
border border-neutral-200 dark:border-black
```
**Variants**: `badge-success` (`bg-success`), `badge-warning` (`bg-warning`), `badge-error` (`bg-error`)
**Plain CSS:**
```css
.badge {
display: inline-block;
width: 0.75rem;
height: 0.75rem;
border-radius: 9999px;
border: 1px solid #e5e5e5;
}
.dark .badge { border-color: #000; }
.badge-success { background: #22C55E; }
.badge-warning { background: #fcd452; }
.badge-error { background: #dc2626; }
```
#### Status Text Pattern
Status indicators combine a badge dot with text:
```html
<div style="display: flex; align-items: center;">
<div class="badge badge-success"></div>
<div style="padding-left: 0.5rem; font-size: 0.75rem; font-weight: 700; color: #22C55E;">
Running
</div>
</div>
```
| Status | Badge Class | Text Color |
|---|---|---|
| Running | `badge-success` | `text-success` (`#22C55E`) |
| Stopped | `badge-error` | `text-error` (`#dc2626`) |
| Degraded | `badge-warning` | `dark:text-warning` (`#fcd452`) |
| Restarting | `badge-warning` | `dark:text-warning` (`#fcd452`) |
---
### 3.8 Dropdown
**Container Tailwind:**
```
p-1 mt-1 bg-white border rounded-sm shadow-sm
dark:bg-coolgray-200 dark:border-coolgray-300 border-neutral-300
```
**Item Tailwind:**
```
flex relative gap-2 justify-start items-center py-1 pr-4 pl-2 w-full text-xs
transition-colors cursor-pointer select-none dark:text-white
hover:bg-neutral-100 dark:hover:bg-coollabs
outline-none focus-visible:bg-neutral-100 dark:focus-visible:bg-coollabs
```
**Plain CSS:**
```css
.dropdown {
padding: 0.25rem;
margin-top: 0.25rem;
background: #fff;
border: 1px solid #d4d4d4;
border-radius: 0.125rem;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.dark .dropdown {
background: #202020;
border-color: #242424;
}
.dropdown-item {
display: flex;
position: relative;
gap: 0.5rem;
justify-content: flex-start;
align-items: center;
padding: 0.25rem 1rem 0.25rem 0.5rem;
width: 100%;
font-size: 0.75rem;
cursor: pointer;
user-select: none;
transition: background-color 150ms;
}
.dropdown-item:hover { background: #f5f5f5; }
.dark .dropdown-item { color: #fff; }
.dark .dropdown-item:hover { background: #6b16ed; }
```
---
### 3.9 Sidebar / Navigation
#### Sidebar Container + Page Layout
The navbar is a **fixed left sidebar** (14rem / 224px wide on desktop), with main content offset to the right.
**Tailwind (sidebar wrapper — desktop):**
```
hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-56 lg:flex-col min-w-0
```
**Tailwind (sidebar inner — scrollable):**
```
flex flex-col overflow-y-auto grow gap-y-5 scrollbar min-w-0
```
**Tailwind (nav element):**
```
flex flex-col flex-1 px-2 bg-white border-r dark:border-coolgray-200 border-neutral-300 dark:bg-base
```
**Tailwind (main content area):**
```
lg:pl-56
```
**Tailwind (main content padding):**
```
p-4 sm:px-6 lg:px-8 lg:py-6
```
**Tailwind (mobile top bar — shown on small screens, hidden on lg+):**
```
sticky top-0 z-40 flex items-center justify-between px-4 py-4 gap-x-6 sm:px-6 lg:hidden
bg-white/95 dark:bg-base/95 backdrop-blur-sm border-b border-neutral-300/50 dark:border-coolgray-200/50
```
**Tailwind (mobile hamburger icon):**
```
-m-2.5 p-2.5 dark:text-warning
```
**Plain CSS:**
```css
/* Sidebar — desktop only */
.sidebar {
display: none;
}
@media (min-width: 1024px) {
.sidebar {
display: flex;
flex-direction: column;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 50;
width: 14rem; /* 224px */
min-width: 0;
}
}
.sidebar-inner {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow-y: auto;
gap: 1.25rem;
min-width: 0;
}
/* Nav element */
.sidebar-nav {
display: flex;
flex-direction: column;
flex: 1;
padding: 0 0.5rem;
background: #fff;
border-right: 1px solid #d4d4d4;
}
.dark .sidebar-nav {
background: #101010;
border-right-color: #202020;
}
/* Main content offset */
@media (min-width: 1024px) {
.main-content { padding-left: 14rem; }
}
.main-content-inner {
padding: 1rem;
}
@media (min-width: 640px) {
.main-content-inner { padding: 1rem 1.5rem; }
}
@media (min-width: 1024px) {
.main-content-inner { padding: 1.5rem 2rem; }
}
/* Mobile top bar — visible below lg breakpoint */
.mobile-topbar {
position: sticky;
top: 0;
z-index: 40;
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
gap: 1.5rem;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
}
.dark .mobile-topbar {
background: rgba(16, 16, 16, 0.95);
border-bottom-color: rgba(32, 32, 32, 0.5);
}
@media (min-width: 1024px) {
.mobile-topbar { display: none; }
}
/* Mobile sidebar overlay (shown when hamburger is tapped) */
.sidebar-mobile {
position: relative;
display: flex;
flex: 1;
width: 100%;
max-width: 14rem;
min-width: 0;
}
.sidebar-mobile-scroll {
display: flex;
flex-direction: column;
padding-bottom: 0.5rem;
overflow-y: auto;
min-width: 14rem;
gap: 1.25rem;
min-width: 0;
}
.dark .sidebar-mobile-scroll { background: #181818; }
```
#### Sidebar Header (Logo + Search)
**Tailwind:**
```
flex lg:pt-6 pt-4 pb-4 pl-2
```
**Logo:**
```
text-2xl font-bold tracking-wide dark:text-white hover:opacity-80 transition-opacity
```
**Search button:**
```
flex items-center gap-1.5 px-2.5 py-1.5
bg-neutral-100 dark:bg-coolgray-100
border border-neutral-300 dark:border-coolgray-200
rounded-md hover:bg-neutral-200 dark:hover:bg-coolgray-200 transition-colors
```
**Search kbd hint:**
```
px-1 py-0.5 text-xs font-semibold
text-neutral-500 dark:text-neutral-400
bg-neutral-200 dark:bg-coolgray-200 rounded
```
**Plain CSS:**
```css
.sidebar-header {
display: flex;
padding: 1rem 0 1rem 0.5rem;
}
@media (min-width: 1024px) {
.sidebar-header { padding-top: 1.5rem; }
}
.sidebar-logo {
font-size: 1.5rem;
font-weight: 700;
letter-spacing: 0.025em;
color: #000;
text-decoration: none;
}
.dark .sidebar-logo { color: #fff; }
.sidebar-logo:hover { opacity: 0.8; }
.sidebar-search-btn {
display: flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.625rem;
background: #f5f5f5;
border: 1px solid #d4d4d4;
border-radius: 0.375rem;
cursor: pointer;
transition: background-color 150ms;
}
.sidebar-search-btn:hover { background: #e5e5e5; }
.dark .sidebar-search-btn {
background: #181818;
border-color: #202020;
}
.dark .sidebar-search-btn:hover { background: #202020; }
.sidebar-search-kbd {
padding: 0.125rem 0.25rem;
font-size: 0.75rem;
font-weight: 600;
color: #737373;
background: #e5e5e5;
border-radius: 0.25rem;
}
.dark .sidebar-search-kbd {
color: #a3a3a3;
background: #202020;
}
```
#### Menu Item List
**Tailwind (list container):**
```
flex flex-col flex-1 gap-y-7
```
**Tailwind (inner list):**
```
flex flex-col h-full space-y-1.5
```
**Plain CSS:**
```css
.menu-list {
display: flex;
flex-direction: column;
flex: 1;
gap: 1.75rem;
list-style: none;
padding: 0;
margin: 0;
}
.menu-list-inner {
display: flex;
flex-direction: column;
height: 100%;
gap: 0.375rem;
list-style: none;
padding: 0;
margin: 0;
}
```
#### Menu Item
**Tailwind:**
```
flex gap-3 items-center px-2 py-1 w-full text-sm
dark:hover:bg-coolgray-100 dark:hover:text-white hover:bg-neutral-300 rounded-sm truncate min-w-0
```
#### Menu Item Active
**Tailwind:**
```
text-black rounded-sm dark:bg-coolgray-200 dark:text-warning bg-neutral-200 overflow-hidden
```
#### Menu Item Icon / Label
```
/* Icon */ flex-shrink-0 w-6 h-6 dark:hover:text-white
/* Label */ min-w-0 flex-1 truncate
```
**Plain CSS:**
```css
.menu-item {
display: flex;
gap: 0.75rem;
align-items: center;
padding: 0.25rem 0.5rem;
width: 100%;
font-size: 0.875rem;
border-radius: 0.125rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.menu-item:hover { background: #d4d4d4; }
.dark .menu-item:hover { background: #181818; color: #fff; }
.menu-item-active {
color: #000;
background: #e5e5e5;
border-radius: 0.125rem;
}
.dark .menu-item-active {
background: #202020;
color: #fcd452;
}
.menu-item-icon {
flex-shrink: 0;
width: 1.5rem;
height: 1.5rem;
}
.menu-item-label {
min-width: 0;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
```
#### Sub-Menu Item
```css
.sub-menu-item {
/* Same as menu-item but with gap: 0.5rem and icon size 1rem */
display: flex;
gap: 0.5rem;
align-items: center;
padding: 0.25rem 0.5rem;
width: 100%;
font-size: 0.875rem;
border-radius: 0.125rem;
}
.sub-menu-item-icon { flex-shrink: 0; width: 1rem; height: 1rem; }
```
---
### 3.10 Callout / Alert
Four types: `warning`, `danger`, `info`, `success`.
**Structure:**
```html
<div class="callout callout-{type}">
<div class="callout-icon"><!-- SVG --></div>
<div class="callout-body">
<div class="callout-title">Title</div>
<div class="callout-text">Content</div>
</div>
</div>
```
**Base Tailwind:**
```
relative p-4 border rounded-lg
```
**Type Colors:**
| Type | Background | Border | Title Text | Body Text |
|---|---|---|---|---|
| **warning** | `bg-warning-50 dark:bg-warning-900/30` | `border-warning-300 dark:border-warning-800` | `text-warning-800 dark:text-warning-300` | `text-warning-700 dark:text-warning-200` |
| **danger** | `bg-red-50 dark:bg-red-900/30` | `border-red-300 dark:border-red-800` | `text-red-800 dark:text-red-300` | `text-red-700 dark:text-red-200` |
| **info** | `bg-blue-50 dark:bg-blue-900/30` | `border-blue-300 dark:border-blue-800` | `text-blue-800 dark:text-blue-300` | `text-blue-700 dark:text-blue-200` |
| **success** | `bg-green-50 dark:bg-green-900/30` | `border-green-300 dark:border-green-800` | `text-green-800 dark:text-green-300` | `text-green-700 dark:text-green-200` |
**Plain CSS (warning example):**
```css
.callout {
position: relative;
padding: 1rem;
border: 1px solid;
border-radius: 0.5rem;
}
.callout-warning {
background: #fefce8;
border-color: #fde047;
}
.dark .callout-warning {
background: rgba(113, 63, 18, 0.3);
border-color: #854d0e;
}
.callout-title {
font-size: 1rem;
font-weight: 700;
}
.callout-warning .callout-title { color: #854d0e; }
.dark .callout-warning .callout-title { color: #fde047; }
.callout-text {
margin-top: 0.5rem;
font-size: 0.875rem;
}
.callout-warning .callout-text { color: #a16207; }
.dark .callout-warning .callout-text { color: #fef08a; }
```
**Icon colors per type:**
- Warning: `text-warning-600 dark:text-warning-400` (`#ca8a04` / `#fcd452`)
- Danger: `text-red-600 dark:text-red-400` (`#dc2626` / `#f87171`)
- Info: `text-blue-600 dark:text-blue-400` (`#2563eb` / `#60a5fa`)
- Success: `text-green-600 dark:text-green-400` (`#16a34a` / `#4ade80`)
---
### 3.11 Toast / Notification
**Container Tailwind:**
```
relative flex flex-col items-start
shadow-[0_5px_15px_-3px_rgb(0_0_0_/_0.08)]
w-full transition-all duration-100 ease-out
dark:bg-coolgray-100 bg-white
dark:border dark:border-coolgray-200
rounded-sm sm:max-w-xs
```
**Plain CSS:**
```css
.toast {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
max-width: 20rem;
background: #fff;
border-radius: 0.125rem;
box-shadow: 0 5px 15px -3px rgba(0, 0, 0, 0.08);
transition: all 100ms ease-out;
}
.dark .toast {
background: #181818;
border: 1px solid #202020;
}
```
**Icon colors per toast type:**
| Type | Color | Hex |
|---|---|---|
| Success | `text-green-500` | `#22c55e` |
| Info | `text-blue-500` | `#3b82f6` |
| Warning | `text-orange-400` | `#fb923c` |
| Danger | `text-red-500` | `#ef4444` |
**Behavior**: Stacks up to 4 toasts, auto-dismisses after 4 seconds, positioned bottom-right.
---
### 3.12 Modal
**Tailwind (dialog-based):**
```
rounded-sm modal-box max-h-[calc(100vh-5rem)] flex flex-col
```
**Modal Input variant container:**
```
relative w-full lg:w-auto lg:min-w-2xl lg:max-w-4xl
border rounded-sm drop-shadow-sm
bg-white border-neutral-200
dark:bg-base dark:border-coolgray-300
flex flex-col
```
**Modal Confirmation container:**
```
relative w-full border rounded-sm
min-w-full lg:min-w-[36rem] max-w-[48rem]
max-h-[calc(100vh-2rem)]
bg-neutral-100 border-neutral-400
dark:bg-base dark:border-coolgray-300
flex flex-col
```
**Plain CSS:**
```css
.modal-box {
border-radius: 0.125rem;
max-height: calc(100vh - 5rem);
display: flex;
flex-direction: column;
}
.modal-input {
position: relative;
width: 100%;
border: 1px solid #e5e5e5;
border-radius: 0.125rem;
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
background: #fff;
display: flex;
flex-direction: column;
}
.dark .modal-input {
background: #101010;
border-color: #242424;
}
/* Desktop sizing */
@media (min-width: 1024px) {
.modal-input {
width: auto;
min-width: 42rem;
max-width: 56rem;
}
}
```
**Modal header:**
```css
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem;
flex-shrink: 0;
}
.modal-header h3 {
font-size: 1.5rem;
font-weight: 700;
}
```
**Close button:**
```css
.modal-close {
width: 2rem;
height: 2rem;
border-radius: 9999px;
color: #fff;
}
.modal-close:hover { background: #242424; }
```
---
### 3.13 Slide-Over Panel
**Tailwind:**
```
fixed inset-y-0 right-0 flex max-w-full pl-10
```
**Inner panel:**
```
max-w-xl w-screen
flex flex-col h-full py-6
border-l shadow-lg
bg-neutral-50 dark:bg-base
dark:border-neutral-800 border-neutral-200
```
**Plain CSS:**
```css
.slide-over {
position: fixed;
top: 0;
bottom: 0;
right: 0;
display: flex;
max-width: 100%;
padding-left: 2.5rem;
}
.slide-over-panel {
max-width: 36rem;
width: 100vw;
display: flex;
flex-direction: column;
height: 100%;
padding: 1.5rem 0;
border-left: 1px solid #e5e5e5;
box-shadow: -10px 0 15px -3px rgba(0, 0, 0, 0.1);
background: #fafafa;
}
.dark .slide-over-panel {
background: #101010;
border-color: #262626;
}
```
---
### 3.14 Tag
**Tailwind:**
```
px-2 py-1 cursor-pointer text-xs font-bold text-neutral-500
dark:bg-coolgray-100 dark:hover:bg-coolgray-300 bg-neutral-100 hover:bg-neutral-200
```
**Plain CSS:**
```css
.tag {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
font-weight: 700;
color: #737373;
background: #f5f5f5;
cursor: pointer;
}
.tag:hover { background: #e5e5e5; }
.dark .tag { background: #181818; }
.dark .tag:hover { background: #242424; }
```
---
### 3.15 Loading Spinner
**Tailwind:**
```
w-4 h-4 text-coollabs dark:text-warning animate-spin
```
**Plain CSS + SVG:**
```css
.loading-spinner {
width: 1rem;
height: 1rem;
color: #6b16ed;
animation: spin 1s linear infinite;
}
.dark .loading-spinner { color: #fcd452; }
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
```
**SVG structure:**
```html
<svg class="loading-spinner" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" opacity="0.25"/>
<path fill="currentColor" opacity="0.75" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
```
---
### 3.16 Helper / Tooltip
**Tailwind (trigger icon):**
```
cursor-pointer text-coollabs dark:text-warning
```
**Tailwind (popup):**
```
hidden absolute z-40 text-xs rounded-sm text-neutral-700 group-hover:block
dark:border-coolgray-500 border-neutral-900 dark:bg-coolgray-400 bg-neutral-200
dark:text-neutral-300 max-w-sm whitespace-normal break-words
```
**Plain CSS:**
```css
.helper-icon {
cursor: pointer;
color: #6b16ed;
}
.dark .helper-icon { color: #fcd452; }
.helper-popup {
display: none;
position: absolute;
z-index: 40;
font-size: 0.75rem;
border-radius: 0.125rem;
color: #404040;
background: #e5e5e5;
max-width: 24rem;
white-space: normal;
word-break: break-word;
padding: 1rem;
}
.dark .helper-popup {
background: #282828;
color: #d4d4d4;
border: 1px solid #323232;
}
/* Show on parent hover */
.helper:hover .helper-popup { display: block; }
```
---
### 3.17 Highlighted Text
**Tailwind:**
```
inline-block font-bold text-coollabs dark:text-warning
```
**Plain CSS:**
```css
.text-highlight {
display: inline-block;
font-weight: 700;
color: #6b16ed;
}
.dark .text-highlight { color: #fcd452; }
```
---
### 3.18 Scrollbar
**Tailwind:**
```
scrollbar-thumb-coollabs-100 scrollbar-track-neutral-200
dark:scrollbar-track-coolgray-200 scrollbar-thin
```
**Plain CSS:**
```css
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #e5e5e5; }
::-webkit-scrollbar-thumb { background: #7317ff; }
.dark ::-webkit-scrollbar-track { background: #202020; }
```
---
### 3.19 Table
**Plain CSS:**
```css
table { min-width: 100%; border-collapse: separate; }
table, tbody { border-bottom: 1px solid #d4d4d4; }
.dark table, .dark tbody { border-color: #202020; }
thead { text-transform: uppercase; }
tr { color: #000; }
tr:hover { background: #e5e5e5; }
.dark tr { color: #a3a3a3; }
.dark tr:hover { background: #000; }
th {
padding: 0.875rem 0.75rem;
text-align: left;
color: #000;
}
.dark th { color: #fff; }
th:first-child { padding-left: 1.5rem; }
td { padding: 1rem 0.75rem; white-space: nowrap; }
td:first-child { padding-left: 1.5rem; font-weight: 700; }
```
---
### 3.20 Keyboard Shortcut Indicator
**Tailwind:**
```
px-2 text-xs rounded-sm border border-dashed border-neutral-700 dark:text-warning
```
**Plain CSS:**
```css
.kbd {
padding: 0 0.5rem;
font-size: 0.75rem;
border-radius: 0.125rem;
border: 1px dashed #404040;
}
.dark .kbd { color: #fcd452; }
```
---
## 4. Base Element Styles
These global styles are applied to all HTML elements:
```css
/* Page */
html, body {
width: 100%;
min-height: 100%;
background: #f9fafb;
font-family: Inter, sans-serif;
}
.dark html, .dark body {
background: #101010;
color: #a3a3a3;
}
body {
min-height: 100vh;
font-size: 0.875rem;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
/* Links */
a:hover { color: #000; }
.dark a:hover { color: #fff; }
/* Labels */
.dark label { color: #a3a3a3; }
/* Sections */
section { margin-bottom: 3rem; }
/* Default border color override */
*, ::after, ::before, ::backdrop {
border-color: #202020; /* coolgray-200 */
}
/* Select options */
.dark option {
color: #fff;
background: #181818;
}
```
---
## 5. Interactive State Reference
### Focus
| Element Type | Mechanism | Light | Dark |
|---|---|---|---|
| Buttons, links, checkboxes | `ring-2` offset | Purple `#6b16ed` | Yellow `#fcd452` |
| Inputs, selects, textareas | Inset box-shadow (4px left bar) | Purple `#6b16ed` | Yellow `#fcd452` |
| Dropdown items | Background change | `bg-neutral-100` | `bg-coollabs` (`#6b16ed`) |
### Hover
| Element | Light | Dark |
|---|---|---|
| Button (default) | `bg-neutral-100` | `bg-coolgray-200` |
| Button (highlighted) | `bg-coollabs` (`#6b16ed`) | `bg-coollabs-100` (`#7317ff`) |
| Button (error) | `bg-red-300` | `bg-red-800` |
| Box card | `bg-neutral-100` + all child text `#000` | `bg-coollabs-100` (`#7317ff`) + all child text `#fff` |
| Coolbox card | Ring: `ring-coollabs` | Ring: `ring-warning` |
| Menu item | `bg-neutral-300` | `bg-coolgray-100` |
| Dropdown item | `bg-neutral-100` | `bg-coollabs` |
| Table row | `bg-neutral-200` | `bg-black` |
| Link | `text-black` | `text-white` |
| Checkbox container | — | `bg-coolgray-100` |
### Disabled
```css
/* Universal disabled pattern */
:disabled {
cursor: not-allowed;
color: #d4d4d4; /* neutral-300 */
background: transparent;
border-color: transparent;
}
.dark :disabled {
color: #525252; /* neutral-600 */
}
/* Input-specific */
.input:disabled {
background: #e5e5e5; /* neutral-200 */
color: #737373; /* neutral-500 */
box-shadow: none;
}
.dark .input:disabled {
background: rgba(24, 24, 24, 0.4);
box-shadow: none;
}
```
### Readonly
```css
.input:read-only {
color: #737373;
background: #e5e5e5;
box-shadow: none;
}
.dark .input:read-only {
color: #737373;
background: rgba(24, 24, 24, 0.4);
box-shadow: none;
}
```
---
## 6. CSS Custom Properties (Theme Tokens)
For use in any CSS framework or plain CSS:
```css
:root {
/* Font */
--font-sans: Inter, sans-serif;
/* Brand */
--color-base: #101010;
--color-coollabs: #6b16ed;
--color-coollabs-50: #f5f0ff;
--color-coollabs-100: #7317ff;
--color-coollabs-200: #5a12c7;
--color-coollabs-300: #4a0fa3;
/* Neutral grays (dark backgrounds) */
--color-coolgray-100: #181818;
--color-coolgray-200: #202020;
--color-coolgray-300: #242424;
--color-coolgray-400: #282828;
--color-coolgray-500: #323232;
/* Warning / dark accent */
--color-warning: #fcd452;
--color-warning-50: #fefce8;
--color-warning-100: #fef9c3;
--color-warning-200: #fef08a;
--color-warning-300: #fde047;
--color-warning-400: #fcd452;
--color-warning-500: #facc15;
--color-warning-600: #ca8a04;
--color-warning-700: #a16207;
--color-warning-800: #854d0e;
--color-warning-900: #713f12;
/* Semantic */
--color-success: #22C55E;
--color-error: #dc2626;
}
```