Appearance
Styling & Theming ​
Tailwind CSS patterns and responsive design conventions.
Tailwind CSS ​
The project uses Tailwind CSS v4 with a custom design system.
For general Tailwind usage, see the official Tailwind documentation.
Container Queries: The Default ​
Critical: Use Container Queries, Not Viewport Queries
Always use container queries (@sm, @md, @lg, etc.) instead of viewport queries (sm, md, lg) for responsive design.
The main content area already has @container/main set up, so all your components respond to the available space, not the viewport size.
Why Container Queries? ​
Your components need to work in:
- Full-width pages
- Sidebars
- Dialogs
- Cards
- Split layouts
Container queries make components responsive to their container size, not the viewport, so they adapt correctly everywhere.
The Main Content Area ​
The app shell already sets up the container:
vue
<!-- AppShell.vue - Already configured -->
<main class="... @container/main">
<slot />
</main>This means your pages automatically get container query support.
Using Container Queries ​
vue
<template>
<!-- âś… Use container queries (responds to parent size) -->
<div class="
flex flex-col
@md:flex-row
@lg:gap-6
@xl:grid
@xl:grid-cols-3
">
<div>Content</div>
</div>
<!-- ❌ Don't use viewport queries (responds to screen size) -->
<div class="
flex flex-col
md:flex-row
lg:gap-6
">
<div>Content</div>
</div>
</template>Container Query Breakpoints ​
| Breakpoint | Min Width | Usage |
|---|---|---|
@sm | 384px | Small containers |
@md | 448px | Medium containers |
@lg | 512px | Large containers |
@xl | 576px | Extra large |
@2xl | 672px | 2X large |
@3xl | 768px | 3X large (common) |
@4xl | 896px | 4X large |
@5xl | 1024px | 5X large |
Common Pattern
Most components switch to multi-column at @3xl (768px container width).
When to Use Viewport Queries ​
Only use viewport queries (sm, md, lg) when you specifically need to respond to screen size, not container size:
vue
<template>
<!-- Show/hide based on screen size -->
<div class="block lg:hidden">
Mobile navigation
</div>
<!-- Different component for mobile vs desktop -->
<UiSheet v-if="isMobile">Mobile</UiSheet>
<UiDialog v-else>Desktop</UiDialog>
</template>
<script setup>
import { useMediaQuery } from '@vueuse/core'
const isMobile = useMediaQuery('(max-width: 768px)')
</script>Design Tokens ​
Use semantic color tokens:
vue
<template>
<!-- âś… Use semantic tokens -->
<div class="bg-card text-card-foreground border-border">
<h1 class="text-heading-foreground">Title</h1>
<p class="text-muted-foreground">Description</p>
</div>
<!-- ❌ Don't use hardcoded colors -->
<div class="bg-white text-black border-gray-200">
<h1 class="text-gray-900">Title</h1>
</div>
</template>Common Tokens:
bg-background/text-foreground- Page colorsbg-card/text-card-foreground- Card colorsbg-primary/text-primary-foreground- Brand colorstext-muted-foreground- Secondary textborder-border- Borderstext-destructive- Errors
Dark Mode
Tokens automatically adapt to dark mode!
RTL Support ​
Always use logical properties for RTL support:
vue
<template>
<!-- âś… RTL-aware -->
<div class="ms-4 me-2 ps-6 pe-4 text-start">
Content
</div>
<!-- ❌ Not RTL-aware -->
<div class="ml-4 mr-2 pl-6 pr-4 text-left">
Content
</div>
</template>Logical Properties:
ms-*/me-*- Margin start/end (not left/right)ps-*/pe-*- Padding start/endstart-*/end-*- Position start/endtext-start/text-end- Text alignmentborder-s-*/border-e-*- Borders
See RTL Support → for complete guide.
Component Styling ​
Use the cn() utility for conditional classes:
vue
<script setup>
import { cn } from '@/lib/utils'
const buttonClass = computed(() => cn(
'px-4 py-2 rounded-md',
props.active && 'bg-primary',
props.disabled && 'opacity-50'
))
</script>See UI Components → for component patterns.
Best Practices ​
Do's âś…
- Use container queries (
@md) as default - Use semantic color tokens
- Use logical properties (
ms-,ps-,text-start) - Use
cn()for conditional classes - See Tailwind docs for utilities
Don'ts ❌
- Don't use viewport queries (
md) unless specifically needed - Don't use hardcoded colors
- Don't use
ml/mr/left/right(breaks RTL) - Don't forget mobile-first approach
Quick Reference ​
vue
<!-- Container query responsive grid -->
<div class="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3 gap-4">
<div>Item</div>
</div>
<!-- RTL-aware spacing -->
<div class="ms-4 ps-6 text-start">Content</div>
<!-- Semantic colors -->
<div class="bg-card text-card-foreground border-border">Card</div>
<!-- Conditional classes -->
<div :class="cn('base-class', condition && 'active-class')">
Content
</div>Related: UI Components → | RTL Support → | Data Tables → | Tailwind Docs →