Skip to content

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

  1. Design Philosophy
  2. Color Palette
  3. Typography
  4. Iconography
  5. Glassmorphism
  6. Layout Principles
  7. Navigation
  8. Buttons & Links
  9. Components
  10. Dark & Light Mode
  11. Spacing & Density
  12. CSS Variables & Foundation
  13. 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

TokenNameHexUsage
--hillsHills#315C4DPrimary brand color, primary actions, active states
--skiesSkies#569AABSecondary actions, informational highlights, links
--bloomsBlooms#F48020Accent color, alerts, attention-drawing elements
--goldenGolden#D2AB4EHigh-priority indicators, badges, premium elements
--muteMute#F9EED5Muted backgrounds (light mode), subtle highlights
--darkDark#181819Dark mode base, primary text on light backgrounds
--mediumMedium#595A5FSecondary text, muted labels, borders
--lightLight#F4F4F0Light 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 NameCSS WeightUse Case
Light300Large display text, hero text only
Regular400Body text, descriptions, default
Medium500Labels, nav items, subtle emphasis
Bold700Headings, section titles, strong emphasis
Black900Page 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.

TokenSizeWeightLine HeightUse Case
--text-page-title20px9001.2Page-level titles
--text-section16px7001.3Section headings
--text-subsection14px7001.3Subsection headings
--text-body13px4001.4Default body text
--text-label12px5001.3Labels, nav items, metadata
--text-caption11px4001.3Captions, timestamps, hints
--text-micro10px5001.2Badges, status indicators

Typography Rules

  • Never exceed 20px for 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

ContextSizeCSS Class
Navigation rail24px.icon-nav
Inline with text18px.icon-inline
Button/action20px.icon-action
Status indicator16px.icon-status
Header/page title24px.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-label or 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:

LevelBackground AlphaBlurBorder AlphaUse Case
glass-base0.4516px0.08Primary panels, sidebar
glass-raised0.5520px0.12Cards, modals, dropdowns
glass-subtle0.2510px0.05Inline sections, sub-panels
glass-solid0.8024px0.15Tooltips, 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-filter prefix for Safari support.
  • Border radius is 6px maximum for panels. Use 4px for 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: 8px to 12px maximum. Never exceed 16px.
  • Gaps between panels: 6px to 8px. Never exceed 12px.
  • Page-level padding: 8px around 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, 40px height. Contains page title, breadcrumbs, search, global actions.
  • Navigation Rail: Fixed left, 48px wide (collapsed), expands to 200px on 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

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;
}
  • 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 --hills color 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.

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>
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);
}
  • 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 --hills color.
  • Icon buttons must always have aria-label. No exceptions.
  • Button padding is 4px 8px maximum. Icon-only buttons are 4px all 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;
}
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.

TokenValueUse Case
--sp-14pxTight gaps — between icon and label, badge padding
--sp-28pxStandard gaps — between elements, panel padding
--sp-312pxComfortable padding — card interiors, top bar
--sp-416pxMaximum internal padding — use sparingly

Density Rules

  • 16px is the hard ceiling for any padding or margin in the application UI.
  • Panel/card internal padding: 8px to 12px.
  • Gap between sibling panels: 6px to 8px.
  • Page gutter (edge of viewport to content): 8px.
  • Stack spacing (vertical gap between stacked items in a list): 2px to 4px.
  • Inline spacing (horizontal gap between adjacent items): 4px to 8px.
  • 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 12px or below
  • Keep gaps at 8px or below
  • Test in both dark and light modes
  • Include aria-label on icon-only buttons
  • Include -webkit-backdrop-filter alongside backdrop-filter

Never Do

  • Add background fills, borders, or outlines to buttons in their default state
  • Use padding larger than 16px anywhere 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-radius larger than 6px
  • Use heavy drop shadows
  • Use decorative whitespace
  • Use font sizes larger than 20px in the application shell
  • Use font sizes smaller than 10px
lock

Enter PIN to continue