skills/cjharmath/web-styling/SKILL.md
Styling patterns for React web applications. Use when working with Tailwind CSS, CSS Modules, theming, responsive design, or component styling.
npx skillsauth add aiskillstore/marketplace web-stylingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
function Button({ variant = 'primary', children }) {
const baseClasses = 'px-4 py-2 rounded-lg font-medium transition-colors';
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-600 text-white hover:bg-red-700',
};
return (
<button className={`${baseClasses} ${variants[variant]}`}>
{children}
</button>
);
}
import { clsx } from 'clsx';
// or with tailwind-merge for deduplication
import { twMerge } from 'tailwind-merge';
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
function Card({ isActive, isDisabled, className, children }) {
return (
<div
className={cn(
'p-4 rounded-lg border',
isActive && 'border-blue-500 bg-blue-50',
isDisabled && 'opacity-50 cursor-not-allowed',
className // Allow overrides
)}
>
{children}
</div>
);
}
// Mobile-first breakpoints
// sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px
<div className="
grid
grid-cols-1 /* Mobile: 1 column */
sm:grid-cols-2 /* Tablet: 2 columns */
lg:grid-cols-3 /* Desktop: 3 columns */
xl:grid-cols-4 /* Large: 4 columns */
gap-4
">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
// Responsive text
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold">
Title
</h1>
// Hide/show at breakpoints
<nav className="hidden md:flex">Desktop Nav</nav>
<nav className="flex md:hidden">Mobile Nav</nav>
// tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media' for OS preference
// ...
};
// Component
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<h1 className="text-black dark:text-white">Title</h1>
<p className="text-gray-600 dark:text-gray-400">Description</p>
</div>
// Toggle dark mode
function ThemeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
document.documentElement.classList.toggle('dark', isDark);
}, [isDark]);
return (
<button onClick={() => setIsDark(!isDark)}>
{isDark ? '☀️' : '🌙'}
</button>
);
}
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
spacing: {
'18': '4.5rem',
'88': '22rem',
},
},
},
};
// Usage
<button className="bg-brand-500 hover:bg-brand-600">
Brand Button
</button>
/* Button.module.css */
.button {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 500;
}
.primary {
background-color: #3b82f6;
color: white;
}
.secondary {
background-color: #e5e7eb;
color: #1f2937;
}
// Button.tsx
import styles from './Button.module.css';
function Button({ variant = 'primary', children }) {
return (
<button className={`${styles.button} ${styles[variant]}`}>
{children}
</button>
);
}
import styles from './Card.module.css';
import { clsx } from 'clsx';
function Card({ isActive, className, children }) {
return (
<div
className={clsx(
styles.card,
isActive && styles.active,
className
)}
>
{children}
</div>
);
}
import styled from 'styled-components';
const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 500;
transition: background-color 0.2s;
${({ variant = 'primary' }) =>
variant === 'primary'
? `
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
}
`
: `
background-color: #e5e7eb;
color: #1f2937;
&:hover {
background-color: #d1d5db;
}
`}
`;
// With theme
import { ThemeProvider } from 'styled-components';
const theme = {
colors: {
primary: '#3b82f6',
secondary: '#e5e7eb',
},
spacing: {
sm: '0.5rem',
md: '1rem',
},
};
const ThemedButton = styled.button`
background-color: ${({ theme }) => theme.colors.primary};
padding: ${({ theme }) => theme.spacing.md};
`;
// Using cva (class-variance-authority) with Tailwind
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
outline: 'border border-gray-300 hover:bg-gray-50',
ghost: 'hover:bg-gray-100',
danger: 'bg-red-600 text-white hover:bg-red-700',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
function Button({ variant, size, className, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
}
// Usage
<Button variant="primary" size="lg">Large Primary</Button>
<Button variant="outline">Outline</Button>
// Built-in animations
<div className="animate-spin">Loading...</div>
<div className="animate-pulse">Loading...</div>
<div className="animate-bounce">Scroll down</div>
// Transitions
<button className="transition-all duration-200 hover:scale-105">
Hover me
</button>
// Custom animation in tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.3s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
};
// Usage
<div className="animate-fade-in">Fading in...</div>
import { motion, AnimatePresence } from 'framer-motion';
function Modal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50"
onClick={onClose}
/>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
className="fixed inset-x-4 top-1/2 -translate-y-1/2 bg-white rounded-lg p-6"
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
);
}
// Centering
<div className="flex items-center justify-center min-h-screen">
<Card>Centered content</Card>
</div>
// Space between
<div className="flex items-center justify-between">
<Logo />
<Navigation />
<UserMenu />
</div>
// Responsive direction
<div className="flex flex-col md:flex-row gap-4">
<Sidebar />
<Main />
</div>
// Equal columns
<div className="grid grid-cols-3 gap-4">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
// Complex layout
<div className="grid grid-cols-12 gap-4">
<aside className="col-span-3">Sidebar</aside>
<main className="col-span-6">Main content</main>
<aside className="col-span-3">Right sidebar</aside>
</div>
// Auto-fit for unknown count
<div className="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
// Centered container with max-width
<div className="container mx-auto px-4">
<Content />
</div>
// Or with custom max-width
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<Content />
</div>
// theme.ts
export const theme = {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
gray: {
50: '#f9fafb',
100: '#f3f4f6',
900: '#111827',
},
},
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
},
} as const;
// CSS Variables approach
:root {
--color-primary: #3b82f6;
--color-background: #ffffff;
--color-text: #111827;
}
.dark {
--color-primary: #60a5fa;
--color-background: #111827;
--color-text: #f9fafb;
}
// Usage with Tailwind
<div className="bg-[var(--color-background)] text-[var(--color-text)]">
Content
</div>
| Issue | Solution |
|-------|----------|
| Styles not applying | Check class specificity, Tailwind purging |
| Dark mode flicker | Use CSS variables or SSR-safe approach |
| Layout shift | Set explicit dimensions, use skeleton loaders |
| Mobile overflow | Add overflow-x-hidden to body |
| Z-index conflicts | Use consistent z-index scale |
styles/
globals.css # Global styles, Tailwind imports
variables.css # CSS custom properties
components/
Button/
Button.tsx
Button.module.css # If using CSS Modules
index.ts
tailwind.config.js # Tailwind configuration
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.