Files
agents/plugins/conductor/templates/code_styleguides/html-css.md
Seth Hobson 1408671cb7 fix(conductor): move plugin to plugins/ directory for proper discovery
Conductor plugin was at root level instead of plugins/ directory,
causing slash commands to not be recognized by Claude Code.
2026-01-15 20:34:57 -05:00

12 KiB

HTML & CSS Style Guide

Web standards for semantic markup, maintainable styling, and accessibility.

Semantic HTML

Document Structure

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Page description for SEO" />
    <title>Page Title | Site Name</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <header>
      <nav aria-label="Main navigation">
        <!-- Navigation -->
      </nav>
    </header>

    <main>
      <article>
        <!-- Primary content -->
      </article>
      <aside>
        <!-- Supplementary content -->
      </aside>
    </main>

    <footer>
      <!-- Footer content -->
    </footer>
  </body>
</html>

Semantic Elements

<!-- Use appropriate semantic elements -->

<!-- Navigation -->
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

<!-- Article with header and footer -->
<article>
  <header>
    <h1>Article Title</h1>
    <time datetime="2024-01-15">January 15, 2024</time>
  </header>

  <p>Article content...</p>

  <footer>
    <p>Written by <address>Author Name</address></p>
  </footer>
</article>

<!-- Sections with headings -->
<section aria-labelledby="features-heading">
  <h2 id="features-heading">Features</h2>
  <p>Section content...</p>
</section>

<!-- Figures with captions -->
<figure>
  <img src="chart.png" alt="Sales data showing 20% growth">
  <figcaption>Q4 2024 Sales Performance</figcaption>
</figure>

<!-- Definition lists -->
<dl>
  <dt>HTML</dt>
  <dd>HyperText Markup Language</dd>
  <dt>CSS</dt>
  <dd>Cascading Style Sheets</dd>
</dl>

Form Elements

<form action="/submit" method="POST">
  <!-- Text input with label -->
  <div class="form-group">
    <label for="email">Email Address</label>
    <input
      type="email"
      id="email"
      name="email"
      required
      autocomplete="email"
      aria-describedby="email-hint"
    />
    <span id="email-hint" class="hint">We'll never share your email.</span>
  </div>

  <!-- Select with label -->
  <div class="form-group">
    <label for="country">Country</label>
    <select id="country" name="country" required>
      <option value="">Select a country</option>
      <option value="us">United States</option>
      <option value="uk">United Kingdom</option>
    </select>
  </div>

  <!-- Radio group with fieldset -->
  <fieldset>
    <legend>Preferred Contact Method</legend>
    <div>
      <input type="radio" id="contact-email" name="contact" value="email" />
      <label for="contact-email">Email</label>
    </div>
    <div>
      <input type="radio" id="contact-phone" name="contact" value="phone" />
      <label for="contact-phone">Phone</label>
    </div>
  </fieldset>

  <!-- Submit button -->
  <button type="submit">Submit</button>
</form>

BEM Naming Convention

Block, Element, Modifier

/* Block: Standalone component */
.card {
}

/* Element: Part of block (double underscore) */
.card__header {
}
.card__body {
}
.card__footer {
}

/* Modifier: Variation (double hyphen) */
.card--featured {
}
.card--compact {
}
.card__header--centered {
}

BEM Examples

<!-- Card component -->
<article class="card card--featured">
  <header class="card__header">
    <h2 class="card__title">Card Title</h2>
  </header>
  <div class="card__body">
    <p class="card__text">Card content goes here.</p>
  </div>
  <footer class="card__footer">
    <button class="card__button card__button--primary">Action</button>
  </footer>
</article>

<!-- Navigation component -->
<nav class="nav nav--horizontal">
  <ul class="nav__list">
    <li class="nav__item nav__item--active">
      <a class="nav__link" href="/">Home</a>
    </li>
    <li class="nav__item">
      <a class="nav__link" href="/about">About</a>
    </li>
  </ul>
</nav>

BEM Best Practices

/* Avoid deep nesting */
/* Bad */
.card__header__title__icon {
}

/* Good - flatten structure */
.card__title-icon {
}

/* Avoid styling elements without class */
/* Bad */
.card h2 {
}

/* Good */
.card__title {
}

/* Modifiers extend base styles */
.button {
  padding: 8px 16px;
  border-radius: 4px;
}

.button--large {
  padding: 12px 24px;
}

.button--primary {
  background: blue;
  color: white;
}

Accessibility

ARIA Attributes

<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true">Status updates appear here</div>

<!-- Landmarks -->
<nav aria-label="Main navigation"></nav>
<nav aria-label="Footer navigation"></nav>

<!-- Current page in navigation -->
<a href="/about" aria-current="page">About</a>

<!-- Expanded/collapsed state -->
<button aria-expanded="false" aria-controls="menu">Toggle Menu</button>
<div id="menu" hidden>Menu content</div>

<!-- Disabled vs aria-disabled -->
<button disabled>Can't click (removed from tab order)</button>
<button aria-disabled="true">Can't click (stays in tab order)</button>

<!-- Loading states -->
<button aria-busy="true">
  <span aria-hidden="true">Loading...</span>
  <span class="visually-hidden">Please wait</span>
</button>

Keyboard Navigation

<!-- Skip link -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<!-- Focusable elements should be obvious -->
<style>
  :focus-visible {
    outline: 2px solid blue;
    outline-offset: 2px;
  }
</style>

<!-- Tabindex usage -->
<!-- tabindex="0": Add to tab order -->
<div tabindex="0" role="button">Custom button</div>

<!-- tabindex="-1": Programmatically focusable only -->
<div id="modal" tabindex="-1">Modal content</div>

<!-- Never use tabindex > 0 -->

Screen Reader Support

/* Visually hidden but accessible */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Hide from screen readers */
[aria-hidden="true"] {
  /* Decorative content */
}
<!-- Icon buttons need accessible names -->
<button aria-label="Close dialog">
  <svg aria-hidden="true"><!-- icon --></svg>
</button>

<!-- Decorative images -->
<img src="decoration.png" alt="" role="presentation" />

<!-- Informative images -->
<img src="chart.png" alt="Sales increased 20% in Q4 2024" />

<!-- Complex images -->
<figure>
  <img
    src="flowchart.png"
    alt="User registration process"
    aria-describedby="flowchart-desc"
  />
  <figcaption id="flowchart-desc">
    Step 1: Enter email. Step 2: Verify email. Step 3: Create password.
  </figcaption>
</figure>

Responsive Design

Mobile-First Approach

/* Base styles for mobile */
.container {
  padding: 16px;
}

.grid {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 24px;
  }

  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Desktop and up */
@media (min-width: 1024px) {
  .container {
    padding: 32px;
    max-width: 1200px;
    margin: 0 auto;
  }

  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

Flexible Units

/* Use relative units */
body {
  font-size: 16px; /* Base size */
}

h1 {
  font-size: 2rem; /* Relative to root */
  margin-bottom: 1em; /* Relative to element */
}

.container {
  max-width: 75ch; /* Character width for readability */
  padding: 1rem;
}

/* Fluid typography */
h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

/* Fluid spacing */
.section {
  padding: clamp(2rem, 5vw, 4rem);
}

Responsive Images

<!-- Responsive image with srcset -->
<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 50vw"
  alt="Description"
  loading="lazy"
/>

<!-- Art direction with picture -->
<picture>
  <source media="(min-width: 1024px)" srcset="hero-desktop.jpg" />
  <source media="(min-width: 768px)" srcset="hero-tablet.jpg" />
  <img src="hero-mobile.jpg" alt="Hero image" />
</picture>

CSS Best Practices

Custom Properties (CSS Variables)

:root {
  /* Colors */
  --color-primary: #0066cc;
  --color-primary-dark: #004c99;
  --color-secondary: #6c757d;
  --color-success: #28a745;
  --color-error: #dc3545;

  /* Typography */
  --font-family-base: system-ui, sans-serif;
  --font-family-mono: ui-monospace, monospace;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.25rem;

  /* Spacing */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;

  /* Borders */
  --border-radius: 4px;
  --border-color: #dee2e6;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --color-primary: #4da6ff;
    --color-background: #1a1a1a;
    --color-text: #ffffff;
  }
}

/* Usage */
.button {
  background: var(--color-primary);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--border-radius);
}

Modern Layout

/* Flexbox for 1D layouts */
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--spacing-md);
}

/* Grid for 2D layouts */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

.header {
  grid-area: header;
}
.sidebar {
  grid-area: sidebar;
}
.main {
  grid-area: main;
}
.footer {
  grid-area: footer;
}

/* Auto-fit grid */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--spacing-lg);
}

Performance

/* Avoid expensive properties in animations */
/* Bad - triggers layout */
.animate-bad {
  animation: move 1s;
}
@keyframes move {
  to {
    left: 100px;
    top: 100px;
  }
}

/* Good - uses transform */
.animate-good {
  animation: move-optimized 1s;
}
@keyframes move-optimized {
  to {
    transform: translate(100px, 100px);
  }
}

/* Use will-change sparingly */
.will-animate {
  will-change: transform;
}

/* Contain for layout isolation */
.card {
  contain: layout style;
}

/* Content-visibility for off-screen content */
.below-fold {
  content-visibility: auto;
  contain-intrinsic-size: 500px;
}

HTML Best Practices

Validation and Attributes

<!-- Use proper input types -->
<input type="email" autocomplete="email" />
<input type="tel" autocomplete="tel" />
<input type="url" />
<input type="number" min="0" max="100" step="1" />
<input type="date" min="2024-01-01" />

<!-- Required and validation -->
<input type="text" required minlength="2" maxlength="50" pattern="[A-Za-z]+" />

<!-- Autocomplete for better UX -->
<input type="text" name="name" autocomplete="name" />
<input type="text" name="address" autocomplete="street-address" />
<input type="text" name="cc-number" autocomplete="cc-number" />

Performance Attributes

<!-- Lazy loading -->
<img src="image.jpg" loading="lazy" alt="Description" />
<iframe src="video.html" loading="lazy"></iframe>

<!-- Preload critical resources -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="hero.jpg" as="image" />
<link rel="preload" href="font.woff2" as="font" crossorigin />

<!-- Preconnect to origins -->
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://analytics.example.com" />

<!-- Async/defer scripts -->
<script src="analytics.js" async></script>
<script src="app.js" defer></script>

Microdata and SEO

<!-- Schema.org markup -->
<article itemscope itemtype="https://schema.org/Article">
  <h1 itemprop="headline">Article Title</h1>
  <time itemprop="datePublished" datetime="2024-01-15"> January 15, 2024 </time>
  <div itemprop="author" itemscope itemtype="https://schema.org/Person">
    <span itemprop="name">Author Name</span>
  </div>
  <div itemprop="articleBody">Article content...</div>
</article>

<!-- Open Graph for social sharing -->
<meta property="og:title" content="Page Title" />
<meta property="og:description" content="Page description" />
<meta property="og:image" content="https://example.com/image.jpg" />
<meta property="og:url" content="https://example.com/page" />