react-styling
Skill0 starsChanged 5 months agoMITView on GitHub
React styling, design tokens, responsive layout, and motion — Tailwind, CSS Modules, CSS-in-JS, design tokens (CSS custom properties), breakpoints, container queries, CSS animations, Framer Motion, dark mode. Use when styling React components, implementing responsive design, building from Figma, adding animations, or setting up a design token system. Triggers on "Tailwind", "CSS", "styling", "responsive", "animation", "motion", "design tokens", "dark mode".
1 other project uses a version of this.
Adopted by
Most recently taken up by
- Nupke/react-claude-skillstheir own version, first seen May 27, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- Nupke/react-claude-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository20 other files it gives its agents.
- react-a11ySkill
- react-api-consumerSkill
- react-bootstrapSkill
- react-brief-recipesSkill
- react-component-designSkill
- react-docs-writingSkill
- react-embedsSkill
- react-forms-validationSkill
- react-html-platformSkill
- react-interview-async-slackSkill
- react-interview-narrationSkill
- react-marketing-patternsSkill
- react-modern-reactSkill
- react-onboarding-docsSkill
- react-performanceSkill
- react-resourcesSkill
- react-routingSkill
- react-senior-uxSkill
- react-testingSkill
- react-utility-snippetsSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 21
Every spacing, color, type-size, border-radius, and shadow value should be a token.
- Distinctive: line 217
Test at 320px wide: If it breaks there, it's not responsive.
- Very specific: line 324
Use IntersectionObserver, not scroll listeners:
- Very specific: line 370
prefers-color-scheme: only: respects system, no toggle. - Distinctive: line 375
Hybrid: default to system, allow override via class.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
react-styling
How a Senior UX Engineer styles React: design-token-driven, responsive by default, motion that respects users, and zero magic numbers.
The "no magic numbers" rule
The fastest senior-vs-mid signal in any review:
// Mid
<div className="mt-[17px] text-[#3a3a3a]">…</div>
// Senior
<div className="mt-4 text-neutral-800">…</div>Every spacing, color, type-size, border-radius, and shadow value should be a token. If you find yourself typing 17px or #3a3a3a, stop. Either there's an existing token close enough, or there's a missing token you should add.
Design tokens via CSS custom properties
This works in any stack (Next, Vite, Astro). Define once, use everywhere:
/* tokens.css */
:root {
/* Color — semantic, not literal */
--color-bg: hsl(0 0% 100%);
--color-fg: hsl(0 0% 10%);
--color-fg-muted: hsl(0 0% 40%);
--color-brand: hsl(173 80% 40%);
--color-brand-hover: hsl(173 80% 35%);
--color-border: hsl(0 0% 90%);
--color-danger: hsl(0 70% 50%);
/* Space scale (8px-based) */
--space-1: 0.25rem; /* 4 */
--space-2: 0.5rem; /* 8 */
--space-3: 0.75rem; /* 12 */
--space-4: 1rem; /* 16 */
--space-6: 1.5rem; /* 24 */
--space-8: 2rem; /* 32 */
--space-12: 3rem; /* 48 */
--space-16: 4rem; /* 64 */
/* Type scale */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.5rem;
--font-size-2xl: 2rem;
--font-size-3xl: 3rem;
/* Radii */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px hsl(0 0% 0% / 0.06);
--shadow-md: 0 4px 12px hsl(0 0% 0% / 0.08);
--shadow-lg: 0 12px 32px hsl(0 0% 0% / 0.12);
/* Motion */
--ease: cubic-bezier(0.4, 0, 0.2, 1);
--duration-fast: 150ms;
--duration-base: 250ms;
--duration-slow: 400ms;
}
[data-theme="dark"] {
--color-bg: hsl(0 0% 7%);
--color-fg: hsl(0 0% 96%);
--color-fg-muted: hsl(0 0% 65%);
--color-border: hsl(0 0% 18%);
}Why CSS custom properties beat JS-only tokens: they cascade, they animate, they switch theme without re-rendering React, and they work in <style> attributes.
Tailwind: the senior playbook
Configure tokens in tailwind.config.ts
import type { Config } from 'tailwindcss';
export default {
content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
bg: 'hsl(var(--color-bg) / <alpha-value>)',
fg: 'hsl(var(--color-fg) / <alpha-value>)',
brand: {
DEFAULT: 'hsl(var(--color-brand) / <alpha-value>)',
hover: 'hsl(var(--color-brand-hover) / <alpha-value>)',
},
},
fontFamily: {
sans: ['var(--font-sans)', 'system-ui', 'sans-serif'],
},
},
},
} satisfies Config;This pattern (Tailwind classes → CSS variables → tokens) is what Radix UI, shadcn/ui, and every modern design system uses. Theme switching is just changing the variable values.
Tailwind patterns to use
cnhelper for conditional classes:import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }<button className={cn('btn', variant === 'primary' && 'btn-primary', className)} />cva(class-variance-authority) for variant components:import { cva } from 'class-variance-authority'; const button = cva('inline-flex items-center justify-center font-medium rounded-md transition', { variants: { variant: { primary: 'bg-brand text-white hover:bg-brand-hover', ghost: 'bg-transparent text-fg hover:bg-fg/5', }, size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4 text-base', }, }, defaultVariants: { variant: 'primary', size: 'md' }, });- Arbitrary values sparingly — if you need
mt-[17px], you probably need a missing token. Usemt-[var(--specific-thing)]if it's a one-off architectural decision.
Tailwind smells
- Long, unreadable class strings → extract a component or use
cva - Inline conditional with
?chains → usecnhelper !important(!) for anything outside of utility overrides → fix specificity instead- Inline arbitrary colors → add a token
CSS Modules (alternative when Tailwind isn't on the table)
/* Button.module.css */
.button {
display: inline-flex;
align-items: center;
padding: var(--space-2) var(--space-4);
background: var(--color-brand);
color: white;
border-radius: var(--radius-md);
transition: background var(--duration-fast) var(--ease);
}
.button:hover { background: var(--color-brand-hover); }
.button:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
.ghost {
background: transparent;
color: var(--color-fg);
}
.ghost:hover { background: hsl(var(--color-fg) / 0.06); }import s from './Button.module.css';
import { cn } from '@/lib/cn';
<button className={cn(s.button, variant === 'ghost' && s.ghost)} />Responsive: mobile-first by default
// Tailwind — mobile-first, breakpoint utilities only ADD constraints upward
<div className="
grid
grid-cols-1 gap-4 p-4
md:grid-cols-2 md:gap-6 md:p-8
lg:grid-cols-3
">/* Plain CSS — mobile first */
.grid { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 768px) {
.grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
}
@media (min-width: 1024px) {
.grid { grid-template-columns: repeat(3, 1fr); }
}Common breakpoints (Tailwind defaults are sensible):
sm640px — phone landscapemd768px — tabletlg1024px — small laptopxl1280px — desktop2xl1536px — large desktop
Test at 320px wide. If it breaks there, it's not responsive.
Container queries (use when the layout depends on the container, not the viewport)
.card-container { container-type: inline-size; }
.card { display: grid; grid-template-columns: 1fr; }
@container (min-width: 400px) {
.card { grid-template-columns: 1fr 2fr; }
}// Tailwind v4 / with @tailwindcss/container-queries plugin
<div className="@container">
<div className="grid grid-cols-1 @md:grid-cols-2">…</div>
</div>Container queries are a strong senior signal because most candidates still use viewport queries everywhere. Use them when a card looks different in a sidebar vs. a full-width grid.
Layout: flex vs. grid
- Grid: 2D layouts, page-level layouts, anything aligned in both rows and columns
- Flex: 1D layouts — a row of buttons, a vertical stack, "push items to the ends"
{/* Common: nav with logo left, items right */}
<nav className="flex items-center justify-between">
<Logo />
<div className="flex items-center gap-4">…</div>
</nav>
{/* Common: card grid, auto-fit */}
<div className="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-6">grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) is one of the most useful patterns: cards reflow without media queries.
Animation — the senior playbook
Animate the cheap properties only
GPU-accelerated, no layout: transform, opacity, filter.
Expensive (cause layout/paint): width, height, top, left, margin.
/* Bad — causes layout on every frame */
.menu-enter { left: -300px; transition: left 0.3s; }
.menu-enter-active { left: 0; }
/* Good — composite-only */
.menu-enter { transform: translateX(-100%); transition: transform 0.3s var(--ease); }
.menu-enter-active { transform: translateX(0); }Always respect prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}CSS transitions for state, keyframes for ongoing motion
/* State change — transition */
.btn { transform: scale(1); transition: transform var(--duration-fast) var(--ease); }
.btn:active { transform: scale(0.98); }
/* Ongoing motion — keyframes */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.skeleton { animation: pulse 1.5s ease-in-out infinite; }Framer Motion (use sparingly, only when CSS can't)
import { motion, AnimatePresence } from 'framer-motion';
<AnimatePresence>
{open && (
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 8 }}
transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}
>
…
</motion.div>
)}
</AnimatePresence>Use Framer when:
- You need exit animations (CSS-only can't animate elements leaving the DOM cleanly)
- You need orchestration (stagger, sequence)
- You need gestures (drag, swipe)
Otherwise CSS wins on bundle size and perf.
Scroll-triggered animation
Use IntersectionObserver, not scroll listeners:
function FadeInOnScroll({ children }: { children: ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(
([entry]) => entry.isIntersecting && setVisible(true),
{ rootMargin: '0px 0px -10% 0px' }
);
io.observe(el);
return () => io.disconnect();
}, []);
return (
<div
ref={ref}
style={{
opacity: visible ? 1 : 0,
transform: visible ? 'translateY(0)' : 'translateY(12px)',
transition: 'opacity 400ms var(--ease), transform 400ms var(--ease)',
}}
>
{children}
</div>
);
}For Tailwind, animation-delay utilities + the new @starting-style or libraries like react-intersection-observer work too.
Dark mode
Three approaches, ranked:
classstrategy + CSS variables (recommended): toggle a class on<html>, all tokens flip via cascade.<html class="dark">
In Tailwind: configure:root { --color-bg: white; } .dark { --color-bg: black; }darkMode: 'class'.prefers-color-schemeonly: respects system, no toggle.@media (prefers-color-scheme: dark) { :root { --color-bg: black; } }- Hybrid: default to system, allow override via class.
Always test:
- Contrast in both modes
- Initial render — prevent flash of wrong theme (FOUC) by setting class on
<html>synchronously before React hydrates (Next.js: a small inline script in<head>)
Modern CSS — the platform features worth knowing
These ship in evergreen browsers and let you delete React/JS in many cases. Most candidates still write 2018-era CSS while these newer features solve the same problems with less code.
:has() — parent selector
/* Card with featured badge → tinted background */
.card:has(.badge--featured) {
background: hsl(173 80% 95%);
}
/* Form with any invalid field → show submit warning */
form:has(:user-invalid) .submit-warning {
display: block;
}
/* Article with an image → adjust padding */
article:has(figure) {
padding-block: 2rem;
}Removes the need for state-driven className toggles in many cases.
:focus-within
.field:focus-within {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
.field:focus-within .hint {
opacity: 1;
}Highlights a container when any descendant has focus — common for form fields, dropdowns, cards.
:user-valid and :user-invalid
input:user-invalid {
border-color: var(--color-danger);
}
input:user-valid {
border-color: var(--color-success);
}Matches after the user has interacted. Unlike :invalid, which matches on blank required fields before the user even tries.
light-dark() and color-scheme
:root {
color-scheme: light dark;
--color-bg: light-dark(white, hsl(0 0% 7%));
--color-fg: light-dark(hsl(0 0% 10%), hsl(0 0% 96%));
}Theme switching without @media (prefers-color-scheme) blocks. color-scheme also tells the browser to use light/dark form controls + scrollbars.
color-mix() — derive colors at the CSS layer
:root {
--color-brand: hsl(173 80% 40%);
--color-brand-hover: color-mix(in oklch, var(--color-brand), black 10%);
--color-brand-tint: color-mix(in oklch, var(--color-brand), white 90%);
}Define one brand color, derive hover/tint/shade without a build step. in oklch keeps the mix perceptually uniform.
@layer — order cascade explicitly
@layer reset, base, components, utilities;
@layer base {
h1 { font-size: 2rem; }
}
@layer components {
.card { padding: 1rem; }
}
@layer utilities {
.text-center { text-align: center; }
}Layers listed later win, regardless of specificity. Predictable cascade without specificity wars or !important.
@scope — scope styles without naming hacks
@scope (.card) {
h2 { font-size: 1.5rem; } /* only h2 inside .card */
}CSS-Modules-like scoping without the build step. Chromium 118+, Safari/Firefox behind flag — keep as progressive enhancement.
text-wrap: balance and text-wrap: pretty
h1, h2, h3 { text-wrap: balance; } /* even line lengths for headings */
p { text-wrap: pretty; } /* avoid widow words */Tiny additions, huge typographic upgrade.
interpolate-size: allow-keywords
details {
interpolate-size: allow-keywords;
}
details::details-content {
block-size: 0;
transition: block-size 0.3s ease;
}
details[open]::details-content {
block-size: auto;
}Finally enables smooth transition to/from auto — accordion open/close without JS measurement. Chromium 131+ only as of mid-2026.
scrollbar-gutter: stable
html {
scrollbar-gutter: stable;
}Reserves scrollbar space even when not visible. Prevents layout jumps when content height changes (e.g., modal opens → scrollbar disappears → page jumps).
Anchor positioning (cross-reference)
For positioning popovers/tooltips relative to triggers without JS — see react-html-platform for the full pattern.
Browser support cheat (mid-2026)
:has()— universal:focus-within— universal:user-valid/:user-invalid— universallight-dark()— universalcolor-mix()— universal@layer— universal@scope— Chromium 118+, others behind flagtext-wrap: balance— universaltext-wrap: pretty— Chromium 117+, others catching upinterpolate-size: allow-keywords— Chromium 131+ onlyscrollbar-gutter— universal- Anchor positioning — Chromium 125+, others behind flag
For not-yet-universal features, fallback is usually "no animation" or "default positioning" — degrades gracefully.
Common styling mistakes (interviewer-visible)
| Mistake | Fix |
style={{ color: '#3a3a3a' }} | Use a token |
transform: scale(0.95) on click but no transition | Add transition: transform 150ms ease |
Hover-only states (no focus-visible) | Always pair hover with focus |
outline: none without replacement | Use :focus-visible { outline: 2px solid var(--color-brand); } |
Fixed width: 320px | Use max-width: 320px; width: 100% |
100vh on mobile breaks (browser chrome) | Use 100dvh or 100svh |
Animating width for a sliding menu | Animate transform: translateX |
Setting font-size: 14px everywhere | Use a type scale, set base on <html> and rem elsewhere |
position: absolute without position: relative parent | Always pair |
| Forgetting hover/focus/active/disabled | Run through all 4 states for every interactive |
Narration phrases
- "I'm using CSS custom properties for tokens so theme switching is a single class toggle on
<html>— no React re-render needed." - "All animation is on
transformandopacity— those run on the compositor and don't trigger layout." - "I added
prefers-reduced-motionbecause the hero animation isn't essential to the content." - "I'm using container queries here because this card lives in both a 3-column grid and a sidebar — viewport breakpoints would lie."
- "The breakpoints are mobile-first; the only place I had to override down was the sticky nav at < 480px."
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

