Appearance
06_STYLE_GUIDE
A comprehensive design and development style guide for building glassmorphic web applications and dashboards. This document defines the visual language, component patterns, layout rules, and implementation code to ensure consistency across all development.
Table of Contents
- Design Philosophy
- Color Palette
- Typography
- Iconography
- Glassmorphism
- Layout Principles
- Navigation
- Buttons & Links
- Components
- Dark & Light Mode
- Spacing & Density
- CSS Variables & Foundation
- Reusable Component Code
1. Design Philosophy
Every design decision must prioritize density, clarity, and efficiency. Users should see the maximum amount of actionable content at all times with zero wasted space.
Core Principles
- Compact & Minimal — All developments must be compact. Maximize usable screen space at all times. No large empty padding, no decorative whitespace, no oversized margins. Every pixel earns its place.
- Streamlined — Interfaces should feel tight and purposeful. Users must be able to see as much information as possible in a single viewport without scrolling.
- Glassmorphic Depth — Use frosted-glass layering to create visual hierarchy without consuming extra space.
- Collapsible Everything — Navigation and secondary UI elements must expand and collapse to yield space to primary content.
- No Button Backgrounds — All interactive elements rely on text, icons, and color — never filled button containers.
2. Color Palette
Semantic Color Tokens
| Token | Name | Hex | Usage |
|---|---|---|---|
--hills | Hills | #315C4D | Primary brand color, primary actions, active states |
--skies | Skies | #569AAB | Secondary actions, informational highlights, links |
--blooms | Blooms | #F48020 | Accent color, alerts, attention-drawing elements |
--golden | Golden | #D2AB4E | High-priority indicators, badges, premium elements |
--mute | Mute | #F9EED5 | Muted backgrounds (light mode), subtle highlights |
--dark | Dark | #181819 | Dark mode base, primary text on light backgrounds |
--medium | Medium | #595A5F | Secondary text, muted labels, borders |
--light | Light | #F4F4F0 | Light mode base, primary text on dark backgrounds |
Usage Rules
- Primary actions always use
--hills. - Links and secondary interactive elements use
--skies. - Destructive or attention-critical actions use
--blooms. - Status indicators and highlights use
--golden. - Never use raw hex values in code. Always reference CSS custom properties.
- Maintain a minimum contrast ratio of 4.5:1 for text against any background.
Opacity Variants
For glassmorphic surfaces, use color tokens at reduced opacity rather than creating new colors:
css
/* Glass surface tints */
--glass-hills: rgba(49, 92, 77, 0.15);
--glass-skies: rgba(86, 154, 171, 0.15);
--glass-blooms: rgba(244, 128, 32, 0.10);
--glass-dark: rgba(24, 24, 25, 0.60);
--glass-light: rgba(244, 244, 240, 0.60);3. Typography
Font Family
Primary Font: Roboto (Google Fonts)
html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap" rel="stylesheet">css
--font-family: 'Roboto', sans-serif;Weight Mapping
| Weight Name | CSS Weight | Use Case |
|---|---|---|
| Light | 300 | Large display text, hero text only |
| Regular | 400 | Body text, descriptions, default |
| Medium | 500 | Labels, nav items, subtle emphasis |
| Bold | 700 | Headings, section titles, strong emphasis |
| Black | 900 | Page titles, primary headings, dashboard headers |
Type Scale
This scale is deliberately compact. Do not inflate sizes. All sizes assume a dashboard/application context where information density matters.
| Token | Size | Weight | Line Height | Use Case |
|---|---|---|---|---|
--text-page-title | 20px | 900 | 1.2 | Page-level titles |
--text-section | 16px | 700 | 1.3 | Section headings |
--text-subsection | 14px | 700 | 1.3 | Subsection headings |
--text-body | 13px | 400 | 1.4 | Default body text |
--text-label | 12px | 500 | 1.3 | Labels, nav items, metadata |
--text-caption | 11px | 400 | 1.3 | Captions, timestamps, hints |
--text-micro | 10px | 500 | 1.2 | Badges, status indicators |
Typography Rules
- Never exceed
20pxfor any UI text in the application shell. Hero or marketing sections are the only exception. - Body text is
13px— not 14 or 16. Density is the priority. - Do not use font sizes below
10px. This is the accessibility floor. - All caps may be used sparingly for micro labels and badges only, with
letter-spacing: 0.05em. - No decorative fonts. Roboto is the only permitted typeface.
4. Iconography
Icon Library
Exclusively use Google Material Icons via CDN. No other icon libraries are permitted.
html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">For outlined variants (preferred for a lighter glassmorphic feel):
html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">Usage
html
<!-- Standard -->
<span class="material-icons">dashboard</span>
<!-- Outlined (preferred) -->
<span class="material-icons-outlined">dashboard</span>Icon Sizing
| Context | Size | CSS Class |
|---|---|---|
| Navigation rail | 24px | .icon-nav |
| Inline with text | 18px | .icon-inline |
| Button/action | 20px | .icon-action |
| Status indicator | 16px | .icon-status |
| Header/page title | 24px | .icon-header |
Icon Rules
- Only Google Material Icons. Do not import Font Awesome, Feather, Lucide, or any other library.
- Use the CDN link. Do not self-host icon fonts unless offline support is a hard requirement.
- Prefer the Outlined variant for general UI. Use the filled variant only for active/selected states (e.g., filled star for favorited, outlined star for unfavorited).
- Every icon used for navigation or action must have an accessible label via
aria-labelor adjacent text.
5. Glassmorphism
Definition
Glassmorphism creates a frosted, translucent glass effect to establish depth, hierarchy, and a sleek modern aesthetic. It relies on semi-transparent surfaces, background blur, subtle borders, and vibrant backgrounds visible through the glass.
Background Treatment
The application uses a full-browser background image with a semi-transparent overlay for readability. The background image spans the entire viewport and remains fixed. All glassmorphic panels float above this image.
css
body {
margin: 0;
min-height: 100vh;
background-image: url('path/to/background.jpg');
background-size: cover;
background-position: center;
background-attachment: fixed;
background-repeat: no-repeat;
}
body::after {
content: '';
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
}
/* Dark mode overlay */
[data-theme="dark"] body::after {
background: rgba(24, 24, 25, 0.75);
}
/* Light mode overlay */
[data-theme="light"] body::after {
background: rgba(244, 244, 240, 0.70);
}Glass Panel Properties
All elevated UI surfaces (cards, panels, modals, dropdowns, nav elements) use the glass effect:
css
.glass {
background: rgba(24, 24, 25, 0.45);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
}
/* Light mode variant */
[data-theme="light"] .glass {
background: rgba(255, 255, 255, 0.50);
border: 1px solid rgba(255, 255, 255, 0.40);
}Glass Hierarchy
Use varying levels of translucency to create depth:
| Level | Background Alpha | Blur | Border Alpha | Use Case |
|---|---|---|---|---|
glass-base | 0.45 | 16px | 0.08 | Primary panels, sidebar |
glass-raised | 0.55 | 20px | 0.12 | Cards, modals, dropdowns |
glass-subtle | 0.25 | 10px | 0.05 | Inline sections, sub-panels |
glass-solid | 0.80 | 24px | 0.15 | Tooltips, critical overlays |
Glassmorphism Rules
- Every floating surface must use a glass style. No opaque cards or panels unless explicitly required for readability (e.g., data-heavy tables).
- Always include the
-webkit-backdrop-filterprefix for Safari support. - Border radius is
6pxmaximum for panels. Use4pxfor smaller elements like chips and badges. - Borders must be subtle. White or light-colored at low opacity. Never use solid or dark borders on glass panels.
- Shadows are optional and minimal. If used:
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15). Never use heavy drop shadows. - Test glass panels against multiple background images to ensure readability in all conditions.
6. Layout Principles
Maximum Density
The layout must maximize the amount of visible, usable content. This is the single most important layout rule.
- Padding inside panels:
8pxto12pxmaximum. Never exceed16px. - Gaps between panels:
6pxto8px. Never exceed12px. - Page-level padding:
8pxaround the content area. - No decorative whitespace. If space exists between elements, it must serve readability — not aesthetics.
Application Shell
The application shell consists of three regions:
┌──────────────────────────────────────────────┐
│ Top Bar (40px height) │
├────┬─────────────────────────────────────────┤
│ │ │
│ R │ Main Content Area │
│ a │ │
│ i │ │
│ l │ │
│ │ │
│48px│ │
│ │ │
├────┴─────────────────────────────────────────┤- Top Bar: Fixed,
40pxheight. Contains page title, breadcrumbs, search, global actions. - Navigation Rail: Fixed left,
48pxwide (collapsed), expands to200pxon hover/click. - Main Content: Fills remaining space. Scrolls independently.
css
.app-shell {
display: grid;
grid-template-columns: 48px 1fr;
grid-template-rows: 40px 1fr;
height: 100vh;
overflow: hidden;
position: relative;
z-index: 1;
}
.top-bar {
grid-column: 1 / -1;
grid-row: 1;
}
.nav-rail {
grid-column: 1;
grid-row: 2;
}
.main-content {
grid-column: 2;
grid-row: 2;
overflow-y: auto;
padding: 8px;
}Content Grid
Inside the main content area, use CSS Grid for dashboard layouts:
css
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 8px;
}Layout Rules
- The viewport is sacred. Users should never feel like they are looking at mostly empty space.
- Scroll only in the main content area. The top bar and nav rail are always fixed.
- No full-width single-column layouts for dashboards. Always use multi-column grids where content allows.
- Panels and cards should fill their grid cells. No centered cards floating in whitespace.
7. Navigation
Navigation Rail (Sidebar)
The primary navigation is an always-visible icon rail that expands on hover or click.
Collapsed state: 48px wide, showing only icons (centered). Expanded state: 200px wide, showing icons + labels, overlaying the content area (not pushing it).
css
.nav-rail {
width: 48px;
transition: width 0.2s ease;
overflow: hidden;
display: flex;
flex-direction: column;
padding: 4px 0;
z-index: 10;
}
.nav-rail:hover,
.nav-rail.expanded {
width: 200px;
position: absolute;
top: 40px;
left: 0;
bottom: 0;
}
.nav-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
color: var(--text-secondary);
text-decoration: none;
white-space: nowrap;
font-size: 12px;
font-weight: 500;
border: none;
background: none;
cursor: pointer;
border-radius: 4px;
transition: color 0.15s ease, background 0.15s ease;
}
.nav-item:hover {
color: var(--text-primary);
background: rgba(255, 255, 255, 0.06);
}
.nav-item.active {
color: var(--hills);
}
.nav-item .material-icons-outlined {
font-size: 24px;
flex-shrink: 0;
width: 24px;
text-align: center;
}
.nav-item-label {
opacity: 0;
transition: opacity 0.15s ease;
}
.nav-rail:hover .nav-item-label,
.nav-rail.expanded .nav-item-label {
opacity: 1;
}html
<nav class="nav-rail glass-base">
<a href="#" class="nav-item active">
<span class="material-icons-outlined">dashboard</span>
<span class="nav-item-label">Dashboard</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">analytics</span>
<span class="nav-item-label">Analytics</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">settings</span>
<span class="nav-item-label">Settings</span>
</a>
</nav>Top Bar
The top bar is a slim, fixed header for contextual info and global actions.
css
.top-bar {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
gap: 12px;
}
.top-bar-title {
font-size: 14px;
font-weight: 700;
white-space: nowrap;
}
.top-bar-actions {
display: flex;
align-items: center;
gap: 4px;
}Collapsible Sections
Any grouped navigation or content that can be collapsed must be collapsible. Use expand/collapse toggles for section groups within the sidebar, filter panels, and secondary menus.
html
<div class="nav-group">
<button class="nav-group-toggle" aria-expanded="true">
<span class="material-icons-outlined" style="font-size:18px;">expand_more</span>
<span class="nav-group-label">Reports</span>
</button>
<div class="nav-group-items">
<a href="#" class="nav-item">
<span class="material-icons-outlined">bar_chart</span>
<span class="nav-item-label">Sales</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">pie_chart</span>
<span class="nav-item-label">Usage</span>
</a>
</div>
</div>css
.nav-group-toggle {
display: flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
background: none;
border: none;
cursor: pointer;
width: 100%;
}
.nav-group-toggle .material-icons-outlined {
transition: transform 0.2s ease;
}
.nav-group-toggle[aria-expanded="false"] .material-icons-outlined {
transform: rotate(-90deg);
}
.nav-group-items {
overflow: hidden;
transition: max-height 0.2s ease;
}
.nav-group-toggle[aria-expanded="false"] + .nav-group-items {
max-height: 0;
}Navigation Rules
- The nav rail is always visible. It never fully disappears.
- Expanded nav overlays content — it does not push or reflow the main layout.
- Active nav item is indicated by
--hillscolor on the icon and label. No background fill. - Group sections must be collapsible via a toggle. Default state should be expanded for the current section, collapsed for others.
- No button backgrounds on any nav element. Hover states use a subtle translucent background (
rgba(255,255,255,0.06)in dark mode). - Icons are required on all top-level nav items. Sub-items may omit icons if space is tight.
8. Buttons & Links
Fundamental Rule
No button backgrounds. All buttons and links display as text, icon + text, or icon only. Color is the interactive indicator — not shape, fill, or border.
Button Variants
css
/* Base button reset */
.btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border: none;
background: none;
cursor: pointer;
font-family: var(--font-family);
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
border-radius: 4px;
transition: color 0.15s ease, background 0.15s ease;
text-decoration: none;
white-space: nowrap;
}
.btn:hover {
color: var(--text-primary);
background: rgba(255, 255, 255, 0.06);
}
/* Primary action — uses Hills color */
.btn-primary {
color: var(--hills);
}
.btn-primary:hover {
color: #3d7360;
background: rgba(49, 92, 77, 0.10);
}
/* Secondary action — uses Skies color */
.btn-secondary {
color: var(--skies);
}
.btn-secondary:hover {
color: #6db3c4;
background: rgba(86, 154, 171, 0.10);
}
/* Danger/attention action — uses Blooms color */
.btn-danger {
color: var(--blooms);
}
.btn-danger:hover {
color: #f69448;
background: rgba(244, 128, 32, 0.10);
}
/* Icon-only button */
.btn-icon {
padding: 4px;
border-radius: 4px;
}
.btn-icon .material-icons-outlined {
font-size: 20px;
}Button HTML Patterns
html
<!-- Icon + Text -->
<button class="btn btn-primary">
<span class="material-icons-outlined">add</span>
New Report
</button>
<!-- Text only -->
<button class="btn btn-secondary">View All</button>
<!-- Icon only -->
<button class="btn btn-icon" aria-label="Settings">
<span class="material-icons-outlined">settings</span>
</button>Link Styling
css
a {
color: var(--skies);
text-decoration: none;
transition: color 0.15s ease;
}
a:hover {
color: #6db3c4;
text-decoration: underline;
}
a:active {
color: var(--hills);
}Button & Link Rules
- Never add a background fill, border, or outline to buttons in their default state. The only background change occurs on hover as a subtle translucent wash.
- All interactive elements must have a visible hover state using a color shift.
- Active/current state uses
--hillscolor. - Icon buttons must always have
aria-label. No exceptions. - Button padding is
4px 8pxmaximum. Icon-only buttons are4pxall around. - Do not use
<button>for navigation. Use<a>for links,<button>for actions. - Disabled state:
opacity: 0.4; pointer-events: none; cursor: default;
9. Components
Cards
css
.card {
padding: 10px;
border-radius: 6px;
display: flex;
flex-direction: column;
gap: 6px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.card-title {
font-size: 14px;
font-weight: 700;
margin: 0;
}
.card-body {
font-size: 13px;
line-height: 1.4;
}Data Tables
Tables prioritize density and scanability.
css
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.data-table th {
text-align: left;
padding: 6px 8px;
font-weight: 500;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--text-muted);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
position: sticky;
top: 0;
z-index: 1;
}
.data-table td {
padding: 5px 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.data-table tr:hover td {
background: rgba(255, 255, 255, 0.03);
}Form Inputs
css
.input {
padding: 5px 8px;
font-size: 13px;
font-family: var(--font-family);
color: var(--text-primary);
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.10);
border-radius: 4px;
outline: none;
transition: border-color 0.15s ease;
}
.input:focus {
border-color: var(--skies);
}
.input::placeholder {
color: var(--text-muted);
}Badges & Chips
css
.badge {
display: inline-flex;
align-items: center;
padding: 1px 6px;
font-size: 10px;
font-weight: 500;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.badge-hills { color: var(--hills); background: rgba(49, 92, 77, 0.15); }
.badge-skies { color: var(--skies); background: rgba(86, 154, 171, 0.15); }
.badge-blooms { color: var(--blooms); background: rgba(244, 128, 32, 0.12); }
.badge-golden { color: var(--golden); background: rgba(210, 171, 78, 0.15); }Tooltips
css
.tooltip {
position: absolute;
padding: 4px 8px;
font-size: 11px;
font-weight: 400;
white-space: nowrap;
border-radius: 4px;
pointer-events: none;
z-index: 100;
/* Uses glass-solid level */
background: rgba(24, 24, 25, 0.85);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.12);
color: var(--light);
}Modals / Dialogs
css
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.50);
display: flex;
align-items: center;
justify-content: center;
z-index: 50;
}
.modal {
width: 90%;
max-width: 480px;
max-height: 80vh;
overflow-y: auto;
padding: 12px;
border-radius: 6px;
/* Uses glass-raised */
background: rgba(24, 24, 25, 0.65);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.modal-title {
font-size: 16px;
font-weight: 700;
}Dropdowns
css
.dropdown {
position: absolute;
min-width: 160px;
padding: 4px 0;
border-radius: 6px;
z-index: 20;
/* Uses glass-raised */
background: rgba(24, 24, 25, 0.65);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.dropdown-item {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
font-size: 13px;
color: var(--text-secondary);
background: none;
border: none;
width: 100%;
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease;
}
.dropdown-item:hover {
color: var(--text-primary);
background: rgba(255, 255, 255, 0.06);
}10. Dark & Light Mode
Implementation
The application defaults to dark mode. A user-togglable theme switch controls the mode via a data-theme attribute on the <html> element.
html
<html data-theme="dark">Theme Variables
css
[data-theme="dark"] {
--bg-overlay: rgba(24, 24, 25, 0.75);
--glass-bg: rgba(24, 24, 25, 0.45);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-hover: rgba(255, 255, 255, 0.06);
--text-primary: #F4F4F0;
--text-secondary: #a8a9ad;
--text-muted: #6e6f74;
--divider: rgba(255, 255, 255, 0.06);
}
[data-theme="light"] {
--bg-overlay: rgba(244, 244, 240, 0.70);
--glass-bg: rgba(255, 255, 255, 0.50);
--glass-border: rgba(255, 255, 255, 0.40);
--glass-hover: rgba(0, 0, 0, 0.04);
--text-primary: #181819;
--text-secondary: #595A5F;
--text-muted: #8e8f93;
--divider: rgba(0, 0, 0, 0.08);
}Theme Toggle
html
<button class="btn btn-icon theme-toggle" aria-label="Toggle theme">
<span class="material-icons-outlined">dark_mode</span>
</button>javascript
const toggle = document.querySelector('.theme-toggle');
const icon = toggle.querySelector('.material-icons-outlined');
toggle.addEventListener('click', () => {
const html = document.documentElement;
const isDark = html.getAttribute('data-theme') === 'dark';
html.setAttribute('data-theme', isDark ? 'light' : 'dark');
icon.textContent = isDark ? 'light_mode' : 'dark_mode';
localStorage.setItem('theme', isDark ? 'light' : 'dark');
});
// Initialize from saved preference
const saved = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('data-theme', saved);Theme Rules
- Dark mode is the default. The application loads in dark mode unless the user has previously selected light mode.
- All color values must be defined as CSS variables under both
[data-theme="dark"]and[data-theme="light"]. - Semantic palette colors (
--hills,--skies, etc.) remain constant across themes. Only surface colors, text colors, and glass properties change. - Test all components in both themes. No component should be built or shipped without verifying it looks correct in both modes.
11. Spacing & Density
Spacing Scale
Use a 4px base unit. All spacing values must be multiples of 4.
| Token | Value | Use Case |
|---|---|---|
--sp-1 | 4px | Tight gaps — between icon and label, badge padding |
--sp-2 | 8px | Standard gaps — between elements, panel padding |
--sp-3 | 12px | Comfortable padding — card interiors, top bar |
--sp-4 | 16px | Maximum internal padding — use sparingly |
Density Rules
16pxis the hard ceiling for any padding or margin in the application UI.- Panel/card internal padding:
8pxto12px. - Gap between sibling panels:
6pxto8px. - Page gutter (edge of viewport to content):
8px. - Stack spacing (vertical gap between stacked items in a list):
2pxto4px. - Inline spacing (horizontal gap between adjacent items):
4pxto8px. - Never use margin-based spacing to create large empty areas. If content doesn't fill the space, add more content or shrink the container.
12. CSS Variables & Foundation
Complete Variable Set
css
:root {
/* Palette */
--hills: #315C4D;
--skies: #569AAB;
--blooms: #F48020;
--golden: #D2AB4E;
--mute: #F9EED5;
--dark: #181819;
--medium: #595A5F;
--light: #F4F4F0;
/* Typography */
--font-family: 'Roboto', sans-serif;
/* Spacing */
--sp-1: 4px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
/* Transitions */
--transition-fast: 0.15s ease;
--transition-normal: 0.2s ease;
/* Borders */
--radius-sm: 3px;
--radius-md: 4px;
--radius-lg: 6px;
/* Shadows (use sparingly) */
--shadow-subtle: 0 2px 8px rgba(0, 0, 0, 0.15);
/* Layout */
--rail-width-collapsed: 48px;
--rail-width-expanded: 200px;
--top-bar-height: 40px;
}Global Reset
css
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: var(--font-family);
font-size: 13px;
line-height: 1.4;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
color: var(--text-primary);
overflow: hidden;
}
a {
color: var(--skies);
text-decoration: none;
}
button {
font-family: inherit;
cursor: pointer;
}
:focus-visible {
outline: 2px solid var(--skies);
outline-offset: 2px;
}
::selection {
background: rgba(86, 154, 171, 0.30);
}13. Reusable Component Code
Full Page Shell
html
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Application</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
<style>
/* === RESET === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
font-family: 'Roboto', sans-serif;
font-size: 13px;
line-height: 1.4;
-webkit-font-smoothing: antialiased;
}
/* === THEME VARIABLES === */
:root {
--hills: #315C4D; --skies: #569AAB; --blooms: #F48020;
--golden: #D2AB4E; --mute: #F9EED5; --dark: #181819;
--medium: #595A5F; --light: #F4F4F0;
}
[data-theme="dark"] {
--bg-overlay: rgba(24,24,25,0.75);
--glass-bg: rgba(24,24,25,0.45);
--glass-border: rgba(255,255,255,0.08);
--glass-hover: rgba(255,255,255,0.06);
--text-primary: #F4F4F0;
--text-secondary: #a8a9ad;
--text-muted: #6e6f74;
--divider: rgba(255,255,255,0.06);
}
[data-theme="light"] {
--bg-overlay: rgba(244,244,240,0.70);
--glass-bg: rgba(255,255,255,0.50);
--glass-border: rgba(255,255,255,0.40);
--glass-hover: rgba(0,0,0,0.04);
--text-primary: #181819;
--text-secondary: #595A5F;
--text-muted: #8e8f93;
--divider: rgba(0,0,0,0.08);
}
/* === BACKGROUND === */
body {
color: var(--text-primary);
overflow: hidden;
min-height: 100vh;
background: url('background.jpg') center/cover fixed no-repeat;
}
body::after {
content: '';
position: fixed;
inset: 0;
background: var(--bg-overlay);
z-index: 0;
pointer-events: none;
}
/* === SHELL LAYOUT === */
.app-shell {
display: grid;
grid-template-columns: 48px 1fr;
grid-template-rows: 40px 1fr;
height: 100vh;
position: relative;
z-index: 1;
}
/* === TOP BAR === */
.top-bar {
grid-column: 1 / -1;
grid-row: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
gap: 12px;
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--glass-border);
}
.top-bar-title {
font-size: 14px;
font-weight: 700;
}
.top-bar-actions {
display: flex;
align-items: center;
gap: 4px;
}
/* === NAV RAIL === */
.nav-rail {
grid-column: 1;
grid-row: 2;
width: 48px;
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-right: 1px solid var(--glass-border);
display: flex;
flex-direction: column;
padding: 4px 0;
transition: width 0.2s ease;
overflow: hidden;
z-index: 10;
}
.nav-rail:hover {
width: 200px;
position: absolute;
top: 40px;
left: 0;
bottom: 0;
}
.nav-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
color: var(--text-secondary);
text-decoration: none;
white-space: nowrap;
font-size: 12px;
font-weight: 500;
border: none;
background: none;
cursor: pointer;
border-radius: 4px;
transition: color 0.15s ease, background 0.15s ease;
}
.nav-item:hover {
color: var(--text-primary);
background: var(--glass-hover);
}
.nav-item.active { color: var(--hills); }
.nav-item .material-icons-outlined {
font-size: 24px;
flex-shrink: 0;
width: 24px;
text-align: center;
}
.nav-item-label {
opacity: 0;
transition: opacity 0.15s ease;
}
.nav-rail:hover .nav-item-label { opacity: 1; }
/* === MAIN CONTENT === */
.main-content {
grid-column: 2;
grid-row: 2;
overflow-y: auto;
padding: 8px;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 8px;
}
/* === GLASS PANELS === */
.glass {
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--glass-border);
border-radius: 6px;
}
/* === CARDS === */
.card {
padding: 10px;
border-radius: 6px;
display: flex;
flex-direction: column;
gap: 6px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title {
font-size: 14px;
font-weight: 700;
}
/* === BUTTONS === */
.btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border: none;
background: none;
cursor: pointer;
font-family: inherit;
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
border-radius: 4px;
transition: color 0.15s ease, background 0.15s ease;
text-decoration: none;
}
.btn:hover {
color: var(--text-primary);
background: var(--glass-hover);
}
.btn-primary { color: var(--hills); }
.btn-primary:hover { color: #3d7360; background: rgba(49,92,77,0.10); }
.btn-secondary { color: var(--skies); }
.btn-secondary:hover { color: #6db3c4; background: rgba(86,154,171,0.10); }
.btn-danger { color: var(--blooms); }
.btn-danger:hover { color: #f69448; background: rgba(244,128,32,0.10); }
.btn-icon { padding: 4px; }
.btn-icon .material-icons-outlined { font-size: 20px; }
/* === LINKS === */
a { color: var(--skies); text-decoration: none; transition: color 0.15s ease; }
a:hover { color: #6db3c4; }
/* === INPUTS === */
.input {
padding: 5px 8px;
font-size: 13px;
font-family: inherit;
color: var(--text-primary);
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.10);
border-radius: 4px;
outline: none;
transition: border-color 0.15s ease;
}
.input:focus { border-color: var(--skies); }
.input::placeholder { color: var(--text-muted); }
/* === BADGES === */
.badge {
display: inline-flex;
align-items: center;
padding: 1px 6px;
font-size: 10px;
font-weight: 500;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.badge-hills { color: var(--hills); background: rgba(49,92,77,0.15); }
.badge-skies { color: var(--skies); background: rgba(86,154,171,0.15); }
.badge-blooms { color: var(--blooms); background: rgba(244,128,32,0.12); }
.badge-golden { color: var(--golden); background: rgba(210,171,78,0.15); }
/* === DATA TABLES === */
.data-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.data-table th {
text-align: left; padding: 6px 8px; font-weight: 500;
font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em;
color: var(--text-muted); border-bottom: 1px solid var(--divider);
}
.data-table td { padding: 5px 8px; border-bottom: 1px solid rgba(255,255,255,0.04); }
.data-table tr:hover td { background: var(--glass-hover); }
/* === FOCUS & SELECTION === */
:focus-visible { outline: 2px solid var(--skies); outline-offset: 2px; }
::selection { background: rgba(86,154,171,0.30); }
</style>
</head>
<body>
<div class="app-shell">
<!-- Top Bar -->
<header class="top-bar">
<span class="top-bar-title">Dashboard</span>
<div class="top-bar-actions">
<button class="btn btn-icon" aria-label="Search">
<span class="material-icons-outlined">search</span>
</button>
<button class="btn btn-icon" aria-label="Notifications">
<span class="material-icons-outlined">notifications</span>
</button>
<button class="btn btn-icon theme-toggle" aria-label="Toggle theme">
<span class="material-icons-outlined">dark_mode</span>
</button>
</div>
</header>
<!-- Navigation Rail -->
<nav class="nav-rail">
<a href="#" class="nav-item active">
<span class="material-icons-outlined">dashboard</span>
<span class="nav-item-label">Dashboard</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">analytics</span>
<span class="nav-item-label">Analytics</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">people</span>
<span class="nav-item-label">Users</span>
</a>
<a href="#" class="nav-item">
<span class="material-icons-outlined">description</span>
<span class="nav-item-label">Reports</span>
</a>
<a href="#" class="nav-item" style="margin-top:auto;">
<span class="material-icons-outlined">settings</span>
<span class="nav-item-label">Settings</span>
</a>
</nav>
<!-- Main Content -->
<main class="main-content">
<div class="dashboard-grid">
<div class="card glass">
<div class="card-header">
<h2 class="card-title">Overview</h2>
<button class="btn btn-icon" aria-label="More options">
<span class="material-icons-outlined">more_vert</span>
</button>
</div>
<div class="card-body">
<p>Content goes here.</p>
</div>
</div>
</div>
</main>
</div>
<script>
// Theme toggle
const toggle = document.querySelector('.theme-toggle');
const icon = toggle.querySelector('.material-icons-outlined');
const saved = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('data-theme', saved);
icon.textContent = saved === 'dark' ? 'dark_mode' : 'light_mode';
toggle.addEventListener('click', () => {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const next = isDark ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
icon.textContent = next === 'dark' ? 'dark_mode' : 'light_mode';
localStorage.setItem('theme', next);
});
</script>
</body>
</html>Quick Reference — Do's and Don'ts
Always Do
- Use CSS custom properties for all colors, spacing, and sizing
- Use Google Material Icons (Outlined) via CDN
- Use Roboto via Google Fonts
- Apply glassmorphic styles to all elevated surfaces
- Make navigation elements collapsible
- Keep padding at
12pxor below - Keep gaps at
8pxor below - Test in both dark and light modes
- Include
aria-labelon icon-only buttons - Include
-webkit-backdrop-filteralongsidebackdrop-filter
Never Do
- Add background fills, borders, or outlines to buttons in their default state
- Use padding larger than
16pxanywhere in the application - Use any icon library other than Google Material Icons
- Use any font other than Roboto
- Use opaque, non-glass surfaces for floating elements
- Use
border-radiuslarger than6px - Use heavy drop shadows
- Use decorative whitespace
- Use font sizes larger than
20pxin the application shell - Use font sizes smaller than
10px