Customization

Styling Guide

Methodology

Vextro follows a Tailwind-first approach. Use utility classes in templates for layout, spacing, colors, and typography. Reserve scoped <style> blocks for patterns that Tailwind cannot express.

When to Use Tailwind

Use Tailwind utility classes for:

  • Layout: flex, grid, items-center, justify-between
  • Spacing: p-4, gap-3, mt-2, mx-auto
  • Colors: text-fg, bg-surface, border-edge, text-primary
  • Typography: text-sm, font-medium, tracking-tight
  • Responsive: sm:flex, lg:grid-cols-3
  • States: hover:bg-surface-emphasis, focus-visible:ring-2
<div class="flex items-center gap-3 p-4 bg-surface border border-edge rounded-lg">
  <span class="text-fg font-medium">Title</span>
  <span class="text-fg-muted text-sm">Description</span>
</div>

When to Use Scoped CSS

Keep scoped <style> blocks for patterns Tailwind cannot handle:

  • :global() selectors for cross-component targeting in Astro
  • Complex pseudo-selectors like ::-webkit-scrollbar, ::before, :has()
  • @keyframes animations and multi-property transitions
  • Media queries with custom logic beyond standard breakpoints
  • SVG data URIs with embedded colors needing dark mode overrides
  • appearance: none with custom background-image overrides
<style>
  /* Scoped CSS -- uses --vx-* tokens */
  .custom-scrollbar {
    scrollbar-width: thin;
    scrollbar-color: rgb(var(--vx-surface-muted)) transparent;
  }

  :global(.dark) .custom-scrollbar {
    scrollbar-color: rgb(var(--neutral-700)) transparent;
  }
</style>

Best Practices

Use semantic tokens for dark mode compatibility

To ensure components work in both light and dark mode, reference --vx-* tokens instead of hardcoded colors:

/* Recommended */
color: rgb(var(--vx-fg));
background: rgb(var(--vx-surface-muted));
border-color: rgb(var(--vx-edge));

/* Avoid -- hardcoded colors break dark mode */
color: #18181b;
background: #f4f4f5;
border-color: #e4e4e7;

Prefer semantic Tailwind classes

<!-- Recommended -->
<div class="text-fg bg-surface border-edge">

<!-- Avoid -- fixed Tailwind colors don't adapt to dark mode -->
<div class="text-zinc-900 bg-white border-zinc-200">

Avoid CSS variable fallbacks

Fallbacks with light-mode defaults can mask missing tokens and break dark mode. If a token is missing, add it to the token system:

/* Avoid -- light-mode fallback masks missing tokens */
color: var(--some-color, #18181b);

/* Recommended -- reference the token directly */
color: rgb(var(--vx-fg));

Opacity support

All --vx-* tokens store space-separated RGB values, enabling opacity modifiers:

/* In Tailwind */
class="bg-primary/10"

/* In scoped CSS */
background: rgb(var(--vx-primary-rgb) / 0.1);

Examples

Before (old token names)

.panel {
  color: rgb(var(--fg-rgb));
  background: rgb(var(--bg-muted-rgb));
  border: 1px solid rgb(var(--border-rgb));
}

After (new --vx-* tokens)

.panel {
  color: rgb(var(--vx-fg));
  background: rgb(var(--vx-surface-muted));
  border: 1px solid rgb(var(--vx-edge));
}

Prefer Tailwind where possible

<!-- Even better -- use Tailwind classes instead of scoped CSS -->
<div class="text-fg bg-surface-muted border border-edge">
Previous
Theming & Dark Mode