Styling Guide¶
Complete guide to CSS architecture and styling patterns in FiberPath GUI.
Overview¶
FiberPath GUI uses a design token system with modular CSS. All styles are centralized in design tokens, and components use CSS modules for scoped styling.
Key Principles:
- Design Tokens: All colors, spacing, typography defined once
- Modular CSS: Component-specific styles in
.module.cssfiles - No CSS-in-JS: Plain CSS for performance and simplicity
- No Utility Classes: Avoid Tailwind-style utilities (semantic class names instead)
Design Tokens¶
Token File (src/styles/tokens.css)¶
All design system variables defined as CSS custom properties:
:root {
/* Colors */
--color-primary: #12a89a;
--color-bg: #09090b;
--color-text: #f7f8fa;
/* Spacing */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 0.75rem; /* 12px */
--spacing-lg: 1rem; /* 16px */
/* Typography */
--font-size-base: 0.875rem; /* 14px */
--font-size-lg: 1rem; /* 16px */
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
}
Token Categories¶
Colors¶
/* Brand */
--color-primary: #12a89a;
--color-primary-hover: #0e8a7e;
/* Backgrounds */
--color-bg: #09090b;
--color-bg-panel: #141416;
--color-bg-hover: #222226;
/* Borders */
--color-border: #242428;
--color-border-focus: var(--color-primary);
/* Text */
--color-text: #f7f8fa;
--color-text-muted: #8f929c;
/* Semantic */
--color-success: #32d2b6;
--color-error: #ff8a8a;
--color-warning: #ffb74d;
--color-info: #64b5f6;
Spacing Scale¶
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 0.75rem; /* 12px */
--spacing-lg: 1rem; /* 16px */
--spacing-xl: 1.5rem; /* 24px */
--spacing-2xl: 2rem; /* 32px */
--spacing-3xl: 3rem; /* 48px */
Usage:
.button {
padding: var(--spacing-sm) var(--spacing-lg);
margin-bottom: var(--spacing-md);
}
Typography¶
/* Font Families */
--font-family-base: "Segoe UI", "Inter", system-ui, sans-serif;
--font-family-mono: "Cascadia Code", "Consolas", monospace;
/* Font Sizes */
--font-size-xs: 0.7rem; /* 11.2px */
--font-size-sm: 0.75rem; /* 12px */
--font-size-base: 0.875rem; /* 14px */
--font-size-md: 0.9375rem; /* 15px */
--font-size-lg: 1rem; /* 16px */
--font-size-xl: 1.25rem; /* 20px */
/* Font Weights */
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
Borders & Radii¶
--border-width-thin: 1px;
--border-width-medium: 2px;
--border-radius-sm: 0.25rem; /* 4px */
--border-radius-md: 0.375rem; /* 6px */
--border-radius-lg: 0.5rem; /* 8px */
--border-radius-xl: 0.75rem; /* 12px */
Shadows¶
--shadow-sm: 0 1px 2px rgb(0, 0, 0, 0.3);
--shadow-md: 0 4px 8px rgb(0, 0, 0, 0.4);
--shadow-lg: 0 8px 16px rgb(0, 0, 0, 0.5);
Transitions¶
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
--transition-slow: 300ms ease;
--transition-colors:
background-color var(--transition-fast), color var(--transition-fast);
--transition-transform: transform var(--transition-base);
Component Styling¶
CSS Modules Pattern¶
File Structure:
src/
├── components/
│ ├── PlanForm.tsx
│ └── PlanForm.module.css
└── styles/
├── tokens.css
└── App.module.css
Component:
import styles from './PlanForm.module.css';
export function PlanForm() {
return (
<div className={styles.container}>
<button className={styles.submitButton}>
Generate G-code
</button>
</div>
);
}
CSS Module:
.container {
padding: var(--spacing-lg);
background-color: var(--color-bg-panel);
border-radius: var(--border-radius-lg);
}
.submitButton {
padding: var(--spacing-sm) var(--spacing-lg);
background-color: var(--color-primary);
color: var(--color-text-inverse);
border: none;
border-radius: var(--border-radius-md);
font-size: var(--font-size-base);
font-weight: var(--font-weight-medium);
cursor: pointer;
transition: var(--transition-colors);
}
.submitButton:hover {
background-color: var(--color-primary-hover);
}
Benefits:
- Scoped class names (no collisions)
- TypeScript autocomplete for class names
- Tree-shakeable (unused styles removed)
State Modifiers¶
.button {
/* Base styles */
}
.button:hover {
/* Hover state */
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.button.primary {
/* Primary variant */
}
.button.secondary {
/* Secondary variant */
}
Usage:
<button className={`${styles.button} ${isPrimary ? styles.primary : styles.secondary}`}>
Click
</button>
Conditional Classes¶
import clsx from 'clsx'; // Optional utility
function Button({ variant, disabled }: ButtonProps) {
return (
<button
className={clsx(
styles.button,
variant === 'primary' && styles.primary,
variant === 'secondary' && styles.secondary,
disabled && styles.disabled
)}
>
Click
</button>
);
}
Layout Patterns¶
Flexbox Layouts¶
.container {
display: flex;
gap: var(--spacing-md);
}
.row {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.column {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
Grid Layouts¶
.formGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-lg);
}
.layerGrid {
display: grid;
grid-template-columns: auto 1fr auto;
gap: var(--spacing-sm);
align-items: center;
}
Panel Container¶
.panel {
background-color: var(--color-bg-panel);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-lg);
padding: var(--spacing-xl);
}
Common Component Patterns¶
Button Styles¶
.button {
padding: var(--spacing-sm) var(--spacing-lg);
border: none;
border-radius: var(--border-radius-md);
font-size: var(--font-size-base);
font-weight: var(--font-weight-medium);
cursor: pointer;
transition: var(--transition-colors);
}
.buttonPrimary {
background-color: var(--color-primary);
color: var(--color-text-inverse);
}
.buttonPrimary:hover {
background-color: var(--color-primary-hover);
}
.buttonSecondary {
background-color: var(--color-bg-hover);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.buttonDanger {
background-color: var(--color-error-red);
color: white;
}
.buttonDanger:hover {
background-color: var(--color-error-red-hover);
}
Input Styles¶
.input {
width: 100%;
padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-md);
color: var(--color-text);
font-size: var(--font-size-base);
font-family: var(--font-family-base);
transition: var(--transition-colors);
}
.input:focus {
outline: none;
border-color: var(--color-border-focus);
}
.input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
Card Styles¶
.card {
background-color: var(--color-bg-panel);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
box-shadow: var(--shadow-sm);
}
.cardHeader {
margin-bottom: var(--spacing-md);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
color: var(--color-text);
}
.cardContent {
color: var(--color-text-muted);
}
Responsive Design¶
Media Queries¶
.container {
padding: var(--spacing-lg);
}
@media (min-width: 768px) {
.container {
padding: var(--spacing-2xl);
}
}
@media (min-width: 1024px) {
.container {
padding: var(--spacing-3xl);
}
}
Container Queries (future)¶
.panel {
container-type: inline-size;
}
.item {
font-size: var(--font-size-sm);
}
@container (min-width: 500px) {
.item {
font-size: var(--font-size-base);
}
}
Animations¶
Hover Effects¶
.button {
transition: var(--transition-colors), var(--transition-transform);
}
.button:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
Loading States¶
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.loading {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
Slide In¶
@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.panel {
animation: slideIn var(--transition-base);
}
Best Practices¶
✅ Do¶
- Use design tokens: Always use
var(--token-name)instead of hardcoded values - Semantic class names:
.submitButtonnot.greenBtn - Modular CSS: One CSS module per component
- Consistent spacing: Use spacing scale (xs, sm, md, lg, xl)
- Accessible colors: Ensure sufficient contrast ratios
❌ Don't¶
- Hardcode values: Don't use
#12a89adirectly, usevar(--color-primary) - Global styles: Avoid global
.buttonclasses (use modules) - Magic numbers: Don't use
17.5px, use tokens or calculate from tokens - !important: Avoid unless absolutely necessary
- Deep nesting: Keep specificity low (max 2-3 levels)
Adding New Tokens¶
Step-by-Step¶
- Add to tokens.css:
:root {
--color-new-feature: #abcdef;
}
- Use in component:
.newFeature {
background-color: var(--color-new-feature);
}
- Document usage:
/* New Feature Colors - Used for XYZ functionality */
--color-new-feature: #abcdef;
--color-new-feature-hover: #98bcde;
Debugging Styles¶
Inspect Element¶
Right-click element → Inspect → Styles tab shows:
- Computed values for CSS variables
- Applied styles from modules
- Inheritance chain
CSS Variable Inspection¶
// In browser console
getComputedStyle(document.documentElement).getPropertyValue("--color-primary");
// Returns: "#12a89a"
Module Class Names¶
In dev mode, class names show source:
<button class="PlanForm_submitButton__abc123"></button>
In production, class names are hashed:
<button class="a1b2c3"></button>
Migration from Inline Styles¶
Before:
<button style={{ backgroundColor: '#12a89a', padding: '8px 16px' }}>
Click
</button>
After:
// Button.module.css
.button {
background-color: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-lg);
}
// Button.tsx
<button className={styles.button}>Click</button>
Benefits: Type safety, reusability, performance.
Next Steps¶
- Performance Guide - Optimizing styles for performance
- Tech Stack Details - Vite CSS handling
- Development Guide - Hot reloading styles