Imported from akintola4/personal-fos-frontend-skill (
personal-fos-frontend-skill/SKILL.md). Install upstream withnpx skills add akintola4/personal-fos-frontend-skill --skill personal-fos-frontend-skill. Copyright stays with the author.
Custom Frontend Skill
This skill is tuned to a specific workflow and aesthetic temperament. Follow it precisely.
When NOT to use this skill
- Backend-only API work with no UI
- React Native or mobile-native projects
- Static HTML/CSS without a component framework
- Design system documentation or Storybook-only work
Step 1: Full Discovery (ALWAYS do this first)
Never start building until you've completed discovery. Ask questions until you're genuinely confident about what to build. The user prefers full discovery upfront with no back-and-forth mid-build.
Group your questions logically and ask them all at once rather than drip-feeding. Cover:
Required discovery areas
Purpose & Content
- What is this page/component/app for? Who will use it?
- What's the primary action or goal for the user?
- What content sections or data does it need to display?
Visual Direction
- Any references, inspiration, or mood? (sites, screenshots, adjectives)
- Any colors, brand guidelines, or existing design system to match?
- Light mode, dark mode, or adaptive (default: adaptive/system preference with both modes implemented)?
Technical Context
- Is this going into an existing Next.js project? If so, are shadcn/ui and Tailwind already set up?
- Any existing components or layout patterns to stay consistent with?
- Any routing, data-fetching, or state requirements?
Typography / Font
- Always ask the user if they have a specific font in mind. Give them three paths:
- Google Font or known font — they name it (e.g. "Geist", "Syne", "DM Serif"), Claude loads it via
next/font/googleorgeist/font - Local font file — they have a
.ttf,.otf,.woff, or.woff2file in their project folder. Ask for the file path and load it vianext/font/local:import localFont from 'next/font/local' const myFont = localFont({ src: './fonts/MyFont.woff2', // path relative to the file using it variable: '--font-custom', }) - No preference — Claude chooses. Default to Geist Sans for UI text, paired with one expressive display font from Google for headings (e.g. Instrument Serif, Syne, DM Serif Display). See Typography section for banned fonts.
- Google Font or known font — they name it (e.g. "Geist", "Syne", "DM Serif"), Claude loads it via
- Always apply the font as a CSS variable on the root layout (
<html className={font.variable}>) and reference it intailwind.config.tsunderfontFamily - If the user provides a local font, always ask: is it a single weight, or do they have multiple weight files? If multiple, use the array format for
srcinnext/font/local - Always output the exact font setup code —
layout.tsxchanges,tailwind.config.tsaddition, and anyglobals.cssvariable reference needed
Theme / Color Palette ← always ask this
-
Ask the user to either:
- Pick a shadcn theme — present a formatted table listing all 13 themes with their primary hue description so the user can choose. If the environment supports it, generate a standalone HTML file the user can open in a browser to preview swatches visually.
- Paste their existing theme — if they already have a
globals.csswith CSS variables (--background,--foreground,--primary, etc.), they can paste it and Claude will use it exactly - Let Claude choose — based on the project's mood and purpose
-
The 13 themes and their primary
--primaryhues for reference:Default— zinc/slate neutralNeutral— pure neutral graySlate— cool blue-grayStone— warm grayZinc— mid neutralGray— standard grayRed— red 600Rose— rose 500Orange— orange 500Green— green 600Blue— blue 600Yellow— yellow 400Violet— violet 600
-
Read
references/shadcn-themes.mdfor exact CSS variable values. Use these when generating theme previews so colors are accurate — not approximate. -
Once the theme is confirmed, apply it consistently via shadcn's CSS variable system — never hardcode hex values in Tailwind classes when a CSS variable exists
-
Always output the full
globals.csstheme block so the user can copy-paste it directly
Border Radius ← always ask this, it's important
- Ask explicitly: rounded corners or sharp corners?
- Rounded: soft, friendly, modern — uses Tailwind's
rounded-lg,rounded-xl,rounded-2xletc. - Sharp: precise, editorial, architectural — uses
rounded-noneorrounded-smat most - This must be applied consistently across ALL components, cards, buttons, inputs, and containers — not mixed
- If using shadcn/ui, override the default radius in
tailwind.config.tsandglobals.css(--radiusCSS variable) to match the choice
Animation Preference
- Ask explicitly: GSAP (professional, timeline-based, high performance) or Framer Motion (React-native, declarative, great for layout transitions)?
- Describe the difference briefly so they can choose confidently (see Animation section below)
Scope
- Is this one component, a full page, or multiple pages?
- Responsive — mobile-first, or desktop-first?
Do not proceed to Step 2 until the user has answered enough to build with confidence.
Step 2: Design Thinking (before writing any code)
Once discovery is complete, articulate your plan briefly to the user:
- The layout structure and content hierarchy
- Typography choices (see Typography section)
- Color/theme approach
- Animation strategy
- Any shadcn/ui components you'll use and why
Get a quick thumbs up or any corrections before building.
Step 3: Build
Stack
- Framework: Next.js (App Router preferred unless user specifies Pages Router)
- Language: TypeScript — always, no exceptions
- Styling: Tailwind CSS utility classes only (no inline styles, no CSS modules unless specifically requested)
- Components: shadcn/ui — real components fetched from shadcn, never hand-rolled recreations
Component Name Recognition
Users may refer to components by many different names — "flyout", "chip", "snackbar", "tray", "wizard", etc. Before building, read references/component-aliases.md to map any unfamiliar name to the correct shadcn/ui component. Never say a component is unknown without checking the alias list first.
shadcn/ui Rules
- Always use actual shadcn/ui components — never write your own Button, Card, Dialog, etc. from scratch
- Reference the correct shadcn import paths:
@/components/ui/button,@/components/ui/card, etc. - Enable all built-in shadcn animations and transitions — do not suppress them. The subtle entrance animations, hover states, and focus rings that come with shadcn components must be active
- If a shadcn component requires a specific variant or prop to show its animation (e.g.
asChild,variant="ghost"), use it correctly - When listing components needed, tell the user exactly which
npx shadcn@latest addcommands to run
Light/Dark Mode
- Always implement both light and dark mode
- Default to system preference via
prefers-color-schemeor Next.js + Tailwind dark mode class strategy - Use CSS variables for all colors (shadcn's default token system is perfect)
- Test that both modes look intentional — not just inverted
Typography
The user's preference is a "perfect mix of custom font or Geist." Apply this hierarchy:
- Geist (Vercel's font, built into Next.js): Use as the default. Clean, geometric, highly legible. Great for body and UI text.
// Next.js 15+ (built-in, no extra package needed) import { Geist, Geist_Mono } from 'next/font/google' // Or via the standalone geist package import { GeistSans } from 'geist/font/sans' import { GeistMono } from 'geist/font/mono' - Custom display font: For headings or hero text where personality matters, pair Geist with one expressive display font loaded via
next/font/google. Good choices: Instrument Serif, Playfair Display, Syne, DM Serif Display, Cabinet Grotesk. - Never use: Inter, Roboto, Arial, system-ui as primary choices — these are too generic.
Type scale: use Tailwind's type utilities but be intentional. Headlines should feel large and confident. Body text should be comfortable to read — text-base or text-lg with leading-relaxed.
Spacing Philosophy
- Generous but not wasteful — sections need room to breathe, but not so much padding they feel empty or disconnected
- Use consistent spacing tokens: prefer
py-16,py-24for sections;gap-6,gap-8for component grids - Avoid: extreme
py-40+padding that creates floating islands of content - Mobile spacing should be tighter: scale down padding responsively with
md:py-24 py-12patterns
Animation Strategy
If user chose GSAP:
- Use
gsap+@gsap/react(useGSAPhook) - ScrollTrigger for scroll-driven animations
- Timeline-based entrance sequences for hero sections
- Preferred patterns:
useGSAP(() => { gsap.from('.hero-title', { y: 40, opacity: 0, duration: 0.8, ease: 'power3.out' }) gsap.from('.hero-sub', { y: 24, opacity: 0, duration: 0.6, delay: 0.2, ease: 'power2.out' }) }) - Keep animations purposeful and fast: 0.4s–0.9s durations, smooth easing. Nothing that makes the user wait.
- Use
will-change: transformsparingly on animated elements
If user chose Framer Motion:
- Use
motioncomponents fromframer-motion AnimatePresencefor enter/exit transitionsuseInView+whileInViewfor scroll reveals- Preferred patterns:
<motion.div initial={{ opacity: 0, y: 32 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, ease: [0.25, 0.1, 0.25, 1] }} viewport={{ once: true }} > - Stagger children with
variantsandstaggerChildren - Layout animations (
layoutprop) for any dynamic resizing
Universal animation rules:
- Depth over decoration: animations should feel like physics, not theater. Things slide in, settle, respond.
- Never animate everything — pick 3–5 key moments per page (hero entrance, section reveals, CTA hover, interactive feedback)
- Respect
prefers-reduced-motion— wrap all animations in a check or use Framer's built-in support - No looping decorative animations that distract from content
Accessibility (always, non-negotiable)
Every component and page must be fully accessible. This is not optional.
- ARIA labels: every interactive element that lacks visible text must have
aria-labeloraria-labelledby. This includes icon buttons, image links, toggles, close buttons, carousels, and custom controls - Screen reader support:
- Use semantic HTML first (
<nav>,<main>,<section>,<header>,<footer>,<article>,<aside>) — never<div>for structural elements - Add
aria-describedbywhere extra context helps (e.g. form fields with helper text) - Use
aria-liveregions for dynamic content that updates (toasts, counters, status messages) - Hide purely decorative elements with
aria-hidden="true" - Visually hidden but screen-reader-visible text uses the
.sr-onlyTailwind class
- Use semantic HTML first (
- Focus management:
- All interactive elements must be keyboard-navigable with visible focus rings — never
outline: nonewithout a custom replacement - Modals and dialogs must trap focus and return it on close (shadcn's Dialog handles this correctly)
- Include a skip-to-main-content link at the top of every full page
- All interactive elements must be keyboard-navigable with visible focus rings — never
- Images: meaningful
alttext always; decorative images getalt="" - Forms: every input must have an associated
<label>— visible or.sr-only; never rely onplaceholderalone - Color contrast: meet WCAG AA minimum (4.5:1 normal text, 3:1 large text) in both light and dark modes
- Roles: use
roleattributes when HTML semantics aren't enough (e.g.role="status",role="alert",role="tablist")
Component Output Format
For each build, output:
- The main component file(s) with full TypeScript types
- A comment block at the top listing all shadcn components needed and their install commands
- Any required additions to
tailwind.config.tsorglobals.css - Brief notes on any environment requirements (GSAP license, env vars, etc.)
Alignment & Consistency (non-negotiable)
This user cares deeply about alignment and consistency. Every build must pass these checks before being considered done.
Spacing Scale — pick one, never deviate
Commit to a base spacing unit before building and use it everywhere:
- Section vertical padding: always the same value across all sections (e.g. always
py-24, neverpy-20on one andpy-28on another) - Internal card padding: always the same (e.g. always
p-6) - Gap between grid/flex items: always the same within a context (e.g. always
gap-6in a card grid) - Never mix arbitrary values (
mt-[42px]) with scale values (mt-10) for the same type of spacing
Grid & Column Alignment
- All content must sit on a consistent column grid — typically
max-w-5xlormax-w-6xlwithmx-auto px-6 - Every section on the page must use the same container class — never one section at
max-w-4xland another atmax-w-6xlunless intentionally full-bleed - Text, cards, and images must share left/right edges — no element should appear to float outside the grid
- Use CSS Grid (not Flexbox wrapping) for repeated equal-height elements like feature cards, pricing tiers, testimonials — Grid guarantees alignment, Flexbox wrap does not
Typography Consistency
- Heading hierarchy must be strict and never skipped: h1 → h2 → h3, never h1 → h3
- The same semantic level must always use the same visual style — every
h2identical, every card title identical - Body text size, weight, and color must be consistent — never
text-smin one paragraph andtext-basein another without reason - Secondary/muted text must always use the same token:
text-muted-foregroundeverywhere — never a mix oftext-gray-500,text-zinc-400, andopacity-60
Component Consistency
- Buttons of the same type must look identical everywhere — same padding, radius, font size
- If a card pattern is established (e.g.
border+p-6+ title + description), every card on the page follows it exactly — no one-off cards with different padding or border treatment - Icon sizes must be consistent within a context — if feature icons are
size-5, they are allsize-5 - Hover and focus states must be consistent — if one button dims on hover, all buttons of that type dim on hover
Visual Rhythm
- Sections should have consistent visual weight — a very heavy section next to a very sparse one feels unbalanced
- Decorative elements (separators, icons, labels) must be consistently placed — always above the heading, or always below, never mixed
Alignment Checklist — run this mentally before outputting code
- Every section uses the same container width
- Spacing between sections is uniform
- Card padding is identical across all cards
- Typography: same level = same style, no exceptions
- All color tokens reference CSS variables, not hardcoded values
- Buttons of the same variant are visually identical across the page
- Grid items align to a shared baseline — CSS Grid used for equal-height rows
- No arbitrary spacing values mixed with scale values
Step 4: After Building
- Briefly explain any non-obvious decisions (font pairing rationale, animation choices, layout tradeoffs)
- List exactly what the user needs to install/run to use the output
- Offer one concrete follow-up: "Want me to add [specific thing that would naturally come next]?"
Card & Surface Design Philosophy (read carefully)
The user's aesthetic is flat and minimal. This is non-negotiable.
Never use:
- ❌ Gradient backgrounds on cards, sections, or any surface
- ❌ Glassy / glassmorphism effects (
backdrop-bluron cards, frosted glass, glass shine overlays) - ❌ Heavy drop shadows (
shadow-xl,shadow-2xl, colored shadows, glow effects) - ❌ Gradient text (
bg-clip-text, rainbow or multi-color text gradients) - ❌ Decorative blobs, orbs, or radial gradient background shapes
- ❌ Shimmer or shine animations on cards
- ❌ Borders with gradient strokes
- ❌ Neumorphism (soft inset shadows)
- ❌ Any card that looks like it was made in 2021 SaaS template style
Always use:
- ✅ Flat solid backgrounds —
bg-background,bg-muted,bg-cardfrom the theme - ✅ Thin, single-color borders —
border border-borderorborder border-border/50 - ✅ Subtle or no shadow —
shadow-smat most, or no shadow at all - ✅ Whitespace and typography to create hierarchy — not decoration
- ✅ Hover states that are simple: a slight background shift (
hover:bg-muted/50) or border color change - ✅ If there's a featured/highlighted card, distinguish it with a slightly different background (
bg-muted) or a stronger border — never a gradient
The test: if a card could appear in a Linear, Vercel, or Notion interface, it's right. If it looks like a crypto landing page or a Figma UI kit showcase, it's wrong.
Anti-patterns — general
- ❌ Hand-roll shadcn components (Button, Input, Card, Dialog, etc.) from scratch
- ❌ Suppress shadcn's built-in animations/transitions
- ❌ Use Inter, Roboto, or Arial as the primary typeface
- ❌ Purple gradients on white backgrounds or other clichéd AI aesthetics
- ❌ Spacing so generous the page feels empty
- ❌ Heavy, slow, looping animations
- ❌ Forget dark mode
- ❌ Skip TypeScript types or use
any - ❌ Hardcode hex colors in Tailwind classes when a CSS variable exists
- ❌ Skip outputting the globals.css theme block
- ❌ Interactive elements without aria-label or visible text
- ❌ Structural layout built with divs instead of semantic HTML
- ❌
outline: nonewithout a focus style replacement - ❌ Images missing alt text
- ❌ Form inputs without associated labels
- ❌ Mix rounded and sharp corners inconsistently across components
- ❌ Forget to set
--radiusin globals.css when user picks sharp corners
Responsive & Mobile Behavior
- Always mobile-first: base styles are mobile, scale up with
sm:,md:,lg:prefixes - Never build desktop-only — every component must be tested mentally at 375px, 768px, and 1280px
- Typography scales down on mobile: if desktop is
text-5xl, mobile istext-3xlortext-4xl— never the same - Navigation: always define what the navbar becomes on mobile — either a hamburger menu opening a sheet/drawer, or a simplified bottom bar. Never leave nav links overflowing on small screens
- Grid columns collapse responsively:
grid-cols-1 sm:grid-cols-2 lg:grid-cols-3— never a fixed multi-column grid with no mobile fallback - Tap targets must be at least 44x44px on mobile — buttons and links must have sufficient padding
- Horizontal scroll is never acceptable — if content overflows, it wraps or collapses
- Spacing tightens on mobile:
py-12 md:py-24,px-4 md:px-6— desktop breathing room doesn't translate to mobile
Image Handling
- Always use Next.js
<Image>component — never a plain<img>tag unless inside an SVG or third-party constraint - Always provide
widthandheightprops, or usefillwith a positioned parent to prevent layout shift - Always define
alttext — meaningful for content images, empty stringalt=""for decorative - Use
placeholder="blur"withblurDataURLfor any above-the-fold image to prevent jarring load - Aspect ratios must be locked with a wrapper:
relative aspect-videooraspect-square— never let images reflow - For user-generated or unknown images, always constrain with
object-coverinside a fixed container - Images in card grids must all be the same aspect ratio — never a mix of portrait and landscape in the same grid
- For hero images: use
priorityprop so they load immediately without lazy-load delay
Loading, Error & Empty States
Every data-dependent component needs three states — never ship just the happy path. Read references/states.md for full patterns covering skeleton loading, error alerts, and empty states.
SEO & Metadata
For every full page (not just components), always output the metadata block. Read references/seo.md for the full template including OpenGraph and Twitter card setup. Never skip metadata for full-page builds — it is part of the deliverable.
Form Patterns
Forms must be built with react-hook-form + zod via shadcn's Form component. Never build uncontrolled forms or manual state forms. Read references/forms.md for validation rules, field state styling, and the required structure template.
Code Output Format & File Structure
Always output code in this structure so the user knows exactly where everything goes:
app/
page.tsx ← route entry point, exports metadata
layout.tsx ← font setup, ThemeProvider, global wrappers
components/
ui/ ← shadcn auto-generated, never manually edited
[feature]/
FeatureName.tsx ← custom components, PascalCase filenames
FeatureName.types.ts ← types/interfaces if complex enough to separate
lib/
utils.ts ← cn() utility and shared helpers
public/
fonts/ ← local font files if applicable
og-image.png ← OG image
- Always specify which file each code block belongs to with a comment at the top:
// components/hero/Hero.tsx - Never dump all code into one file unless it's a single self-contained component under ~100 lines
- Types: define inline for simple props, extract to a
.types.tsfile if a component has 5+ props or shared types - Always include the exact
npx shadcn@latest addcommands needed before the code - Always include the exact
npm installcommands for any non-shadcn dependencies
Handling "Make It Look More X" Requests
When the user says things like "make it feel more premium", "make it cleaner", "make it more editorial", "make it warmer" — translate these into concrete design decisions before touching code.
Translation guide:
- "More premium / luxury" → tighter letter-spacing on headings (
tracking-tight), larger type scale, more whitespace, serif display font, monochromatic palette - "Cleaner / simpler" → reduce visual elements, strip any decorative separators, increase whitespace, reduce font weights to one or two
- "More editorial / magazine" → asymmetric layouts, large headline type, horizontal rules, strong typographic hierarchy, pull quotes
- "Warmer" → Stone or Zinc theme, serif body font, softer border radius, slightly off-white backgrounds (
bg-stone-50) - "More modern / techy" → geometric sans, tighter spacing, monospace accents, sharper radius, darker theme
- "More playful" → increase border radius to
rounded-2xl, slightly larger font sizes, more spacing, lighter palette - "Bolder" → increase heading size by one step, increase font weight, reduce body text color to create stronger contrast with headings
Always confirm the translation: "By 'more premium' I'm thinking: tighter letter-spacing, a serif display font for headings, and more vertical whitespace. Does that match what you have in mind?"
Presenting Multiple Layout Options
When the scope is a full page or a key section and there's genuine ambiguity about layout direction, present 2–3 layout options before building — not after.
Format: describe each option in 2–3 sentences with a clear label. Do not build all of them — get a pick first.
Example:
Option A — Centered editorial: Hero text centered, large serif headline, single CTA below, full-width image underneath. Clean and confident.
Option B — Split layout: Left side headline + CTA, right side image or illustration. Good for showing a product screenshot or visual.
Option C — Asymmetric grid: Headline top-left, supporting text bottom-right, image spanning the gap. More dynamic, editorial feel.
Rules:
- Only offer options when layout direction is genuinely unclear — don't offer options for simple components
- Maximum 3 options — more than that creates decision fatigue
- Each option must be meaningfully different — not just "with or without a subtitle"
- Once the user picks, build only that option fully
Version Notes
When revising code after feedback, start the response with a "Changes in this revision:" block — max 5 specific bullets describing what changed and why. Be concrete ("reduced card padding from p-8 to p-6") not vague ("changed padding"). Never silently change things the user didn't ask for — note proactive improvements as "also improved: ..."