AGilbertDev's styling conventions and visual identity for Nuxt/Tailwind projects — semantic-token theming, Hanken Grotesk type, fluid clamp() sizing, dark mode, and accessible Nuxt UI components. Use when styling components or pages, building responsive layouts, matching the brand, or setting up a theme. Pairs with nuxt-conventions:frontend.
Install
npx skillscat add agilbertdev/claude-plugins/styling Install via the SkillsCat registry.
SKILL.md
Styling conventions (Tailwind / Nuxt UI)
The visual identity shared across AGilbertDev's Nuxt projects (portfolio, time-tracking, resume). Starting defaults, adjust per project. Pairs with nuxt-conventions:frontend for component choices and icons.
Stack & foundation
- Tailwind v4 + Nuxt UI v4.
main.cssis just@import "tailwindcss"; @import "@nuxt/ui";plus a small@theme staticblock. - Utility-first. Avoid custom CSS files. When custom CSS is unavoidable, keep it in
main.css(font var, palette ramps, base tweaks), not scattered across components. - Self-host fonts through
@nuxt/fonts: declare the family in@theme static, no manual<link>.
Brand & color
- Brand sans is Hanken Grotesk (set as
--font-sansin@theme static). Lato is the old print-only font, not for web. - Personal/portfolio palette is teal primary on dark navy, inspired by Nuxt's own system. Product apps pick their own primary.
- One primary + one neutral, themed through Nuxt UI. Set them in
app.config.ts(ui.colors.primary,neutral); never hardcode a second palette. - Define the palette in ONE place: color ramps in
main.css@theme static, semantic mapping inapp.config.ts. A palette change is one edit, not many. - Reserve status colors (
success/info/warning/error) as fixed Nuxt UI colors so they always read the same, never recolored by the active theme. - Derive on-brand accents from the primary with oklch relative color (
oklch(from var(--ui-primary) l c calc(h + 25))to shift the hue toward cyan,calc(l * 1.1)to lighten) rather than hardcoding a second hue, so accents track a retheme.
Semantic tokens, never raw color
- Style with Nuxt UI semantic classes:
bg-default/bg-muted/bg-elevated,text-highlighted/text-muted/text-dimmed/text-primary,ring-default/border-default. In raw CSS use the vars (--ui-text,--ui-primary,--ui-bg,--ui-border). - A hardcoded hex is acceptable only for a one-off decorative surface (for example a fixed image backdrop), never for text or UI chrome.
Theming & atmospheres (optional, advanced)
- For multi-theme apps, define each theme as a
[data-theme="x"]block inmain.cssthat overrides the--ui-color-primary-*and--ui-color-neutral-*50–950 ramps. Nuxt UI derives every surface, border, text, and hover shade from those. - Drive it from a
useThemecomposable: independent light and dark picks persisted in separate cookies, applied to<html data-theme>, with a synchronous no-flash inline script inapp.vueso the correct theme paints first.
Dark mode
- Every project supports light + dark via
useColorMode. Design both, not just one. - Logos: either swap two SVGs (
dark:hidden/hidden dark:block) or use one inline SVG colored withvar(--ui-text)/var(--ui-primary)so it adapts to theme and mode with no extra files. - Guard color-mode-dependent UI with
<ClientOnly>and a sized fallback to avoid a hydration mismatch. - On very dark canvases, lift
--ui-bordertoward neutral-600 so nav, footer, and card separators stay visible.
Typography
- Larger base text:
body { font-size: var(--text-lg) }, and default Nuxt UI components tosize: 'md'(withtext-md) inapp.config.tsso the whole UI scales up together. - Big titles:
font-extrabold tracking-tight. Section titles:font-semibold. Small eyebrow/label text:uppercase tracking-wideortracking-widest, oftentext-primary. - Body paragraphs:
text-justifyandhyphens-none. Never break a word with a hyphen anywhere on the site. Applies to prose and descriptions, not to centered headings or short subtitles.
Fluid, single-viewport sizing (signature)
- Pages are built to fit one viewport without scrolling on desktop: wrap content in
min-h-full flex items-centerand let it scale to fit. - Scale type, spacing, gaps, and image sizes with
clamp()rather than fixedsm:/lg:steps. Pattern:text-[clamp(1.5rem,1.6vw+0.5rem,3.5rem)],gap-[clamp(0.75rem,2vh,1.5rem)],size-[clamp(10rem,28vw,16rem)]— a mobile minimum, a fluid middle, a large-screen maximum. - Mobile-first otherwise: use
min-h-dvh(notmin-h-screen), and step padding and margin up atsm:andlg:.
Layout
- Build the shell from Nuxt UI Pro layout components:
UHeader+UMain+UFooter; pages withUPage/UPageBody/UPageHeader/UPageHero; nav viaUNavigationMenu; dividers viaUSeparator. - Horizontal padding scale:
px-6 sm:px-6 lg:px-8(a touch more on mobile). Set it once globally throughui.containerinapp.config.tsso every section agrees. - Space stacked sections with fluid vertical padding, for example
py-[clamp(4rem,10vh,8rem)]. - Container widths step up:
lg:max-w-5xl xl:max-w-6xl 2xl:max-w-7xlfor wide layouts,max-w-2xlfor prose,lg:max-w-mdfor forms. Center withmx-auto.
Components
- Buttons:
UButtonwithcolor+variant. Primary action is solid primary, often with a leadingiconand a bold trailing arrow (trailingIcon="i-ph-arrow-right-bold"). Secondary iscolor="neutral" variant="outline". Icon-only (social links, toggles) isvariant="ghost"plus anaria-label. Register custom sizes inapp.config.tsfor oversized CTAs (for example a2xl); make oversized buttons scale down on mobile (text-base ... sm:text-xl) and stack full width so long labels never truncate. - Cards:
rounded-2xl bg-default ring ring-default(a ring, not a border),hover:ring-primary; on media,group-hover:scale-105withtransition. - Round media (avatars, portraits): size by width with
aspect-square(w-fullinside a width-bounded wrapper), never by height withw-auto. Height-driven sizing stretches into an oval the moment a narrow column constrains the width. It must always be a perfect circle. - Tags:
UBadge color="neutral" variant="subtle" size="sm". - Forms:
UForm+UFormField+UInput/UTextarea(class="w-full"),space-y-4, avalidatefunction returning an errors array, realautocompletevalues, a right-aligned submit button with a:loadingstate, andUAlert variant="subtle"for success and error.
Accessibility (non-negotiable)
- Skip-to-content link:
sr-only focus:not-sr-only, targeting atabindex="-1"UMain#main-content. - Focus ring on every interactive element:
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary. aria-labelon every icon-only button and link.- Semantic HTML for every interactive element.
<button>,<nav>,<main>,<article>. Never a<div>as a control. - Lean on semantic tokens so contrast holds in both light and dark.
Motion
- Subtle and purposeful only (a card hover scale, a flip on the locale toggle). Always gate transitions behind
@media (prefers-reduced-motion: reduce).
Signature glow (optional, on-brand)
- A slow spinning gradient border is the signature accent. Implement it once as a
.btn-glowutility inmain.css: an absolutely-positioned::beforewithborder-radius: inherit, aconic-gradientmasked into a thin ring (mask+mask-composite: exclude), an animatable--btn-anglethrough CSS@propertyspinning slowly (about 7s), and a softbox-shadowglow. Reveal it on:hover; a.glow-onmodifier keeps it lit permanently (used around the portrait). Gate the spin behindprefers-reduced-motion. Put the ring::beforeabove the content (z-index: 1) so it shows over media that reaches the edge. - Keep the ring on-brand and legible on any button color: sweep between the brand colors (teal and a hue-shifted cyan) with a lighter glint, all derived from the primary via oklch relative color so nothing hardcodes a second hue.
- A faint page-level glow (a large, low-opacity radial of the primary, hue-shifted) anchored top-right and bled down through the first sections adds depth. Keep it fainter on the dark canvas so it never muddies the navy.
The design section of a spec
- Nuxt UI primitives first, then custom Tailwind layout. Name components exactly, like
UPageSectionholding aUCardwithrounded-2xl bg-default ring ring-default. - Semantic tokens and
clamp()sizing for every decision listed. Reach for.btn-glowon primary calls to action, cards, and round media. - Prose and class lists only. No
<template>, no<script setup>. - Never reference a Figma asset URL. Note that assets are downloaded to
public/images/before use.