Implements CSS-in-JS styling with styled-components using tagged template literals, theming, and dynamic props. Use when wanting component-scoped CSS-in-JS, dynamic styling based on props, or theming support.
Creates styled-components using tagged template literals for React. Use when building component-scoped CSS-in-JS with dynamic props, theming, or animations.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/patterns.mdreferences/ssr.mdCSS-in-JS library using tagged template literals to style React components.
Install:
npm install styled-components
Create styled components:
import styled from 'styled-components';
const Button = styled.button`
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
background: #3b82f6;
color: white;
&:hover {
background: #2563eb;
}
`;
const Container = styled.div`
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
`;
function App() {
return (
<Container>
<Button>Click me</Button>
</Container>
);
}
Props prefixed with $ are filtered from DOM:
interface ButtonProps {
$variant?: 'primary' | 'secondary';
$size?: 'sm' | 'md' | 'lg';
}
const Button = styled.button<ButtonProps>`
padding: ${props => {
switch (props.$size) {
case 'sm': return '8px 16px';
case 'lg': return '16px 32px';
default: return '12px 24px';
}
}};
background: ${props => props.$variant === 'secondary' ? '#e5e7eb' : '#3b82f6'};
color: ${props => props.$variant === 'secondary' ? '#1f2937' : 'white'};
`;
// Usage
<Button $variant="secondary" $size="lg">
Secondary Button
</Button>
const Card = styled.div<{ $elevated?: boolean; $selected?: boolean }>`
padding: 16px;
border-radius: 8px;
background: white;
${props => props.$elevated && `
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
`}
${props => props.$selected && `
border: 2px solid #3b82f6;
`}
`;
const Button = styled.button`
padding: 12px 24px;
border: 2px solid #3b82f6;
border-radius: 8px;
background: transparent;
color: #3b82f6;
cursor: pointer;
`;
// Extend with additional styles
const PrimaryButton = styled(Button)`
background: #3b82f6;
color: white;
`;
const DangerButton = styled(Button)`
border-color: #ef4444;
color: #ef4444;
&:hover {
background: #ef4444;
color: white;
}
`;
Change the rendered element with as:
const Button = styled.button`
padding: 12px 24px;
text-decoration: none;
display: inline-block;
`;
// Render as anchor
<Button as="a" href="/about">About</Button>
// Render as Link (React Router)
<Button as={Link} to="/about">About</Button>
import { Link } from 'react-router-dom';
// Component must accept and pass className
const StyledLink = styled(Link)`
color: #3b82f6;
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;
// Custom component
function CustomCard({ className, children }: { className?: string; children: React.ReactNode }) {
return <div className={className}>{children}</div>;
}
const StyledCard = styled(CustomCard)`
padding: 16px;
border-radius: 8px;
`;
Attach default or computed attributes:
const Input = styled.input.attrs<{ $size?: string }>(props => ({
type: 'text',
placeholder: props.placeholder || 'Enter text...',
}))`
padding: ${props => props.$size === 'large' ? '16px' : '12px'};
border: 1px solid #e5e7eb;
border-radius: 6px;
`;
// Password input with overridden type
const PasswordInput = styled(Input).attrs({
type: 'password',
placeholder: 'Enter password',
})``;
import styled, { keyframes } from 'styled-components';
const fadeIn = keyframes`
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
const spin = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const FadeInDiv = styled.div`
animation: ${fadeIn} 0.3s ease-out;
`;
const Spinner = styled.div`
width: 24px;
height: 24px;
border: 2px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: ${spin} 0.8s linear infinite;
`;
import styled, { ThemeProvider } from 'styled-components';
const theme = {
colors: {
primary: '#3b82f6',
secondary: '#6b7280',
success: '#10b981',
error: '#ef4444',
background: '#ffffff',
text: '#1f2937',
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px',
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '16px',
full: '9999px',
},
};
type Theme = typeof theme;
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
// Access theme in components
const Button = styled.button`
background: ${props => props.theme.colors.primary};
padding: ${props => props.theme.spacing.md};
border-radius: ${props => props.theme.borderRadius.md};
`;
// Wrap app
function App() {
return (
<ThemeProvider theme={theme}>
<Button>Themed Button</Button>
</ThemeProvider>
);
}
import { useTheme } from 'styled-components';
function Component() {
const theme = useTheme();
return (
<div style={{ color: theme.colors.primary }}>
Using theme in regular component
</div>
);
}
const lightTheme = {
colors: {
background: '#ffffff',
text: '#1f2937',
primary: '#3b82f6',
},
};
const darkTheme = {
colors: {
background: '#1f2937',
text: '#f9fafb',
primary: '#60a5fa',
},
};
function App() {
const [isDark, setIsDark] = useState(false);
return (
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<AppContainer>
<button onClick={() => setIsDark(!isDark)}>Toggle Theme</button>
</AppContainer>
</ThemeProvider>
);
}
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
background: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
}
a {
color: ${props => props.theme.colors.primary};
}
`;
function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<MainContent />
</ThemeProvider>
);
}
Share style fragments:
import styled, { css } from 'styled-components';
const flexCenter = css`
display: flex;
align-items: center;
justify-content: center;
`;
const truncate = css`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const Card = styled.div`
${flexCenter}
padding: 16px;
`;
const Title = styled.h2`
${truncate}
max-width: 200px;
`;
Reference other styled components:
const Icon = styled.span`
color: #6b7280;
transition: color 0.2s;
`;
const Button = styled.button`
display: flex;
align-items: center;
gap: 8px;
&:hover ${Icon} {
color: #3b82f6;
}
`;
// Usage
<Button>
<Icon>+</Icon>
Add Item
</Button>
$ to avoid DOM warningsstyled(Component) for variantsDefaultTheme for autocompleteconst breakpoints = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
};
const Container = styled.div`
padding: 16px;
@media (min-width: ${breakpoints.md}) {
padding: 24px;
}
@media (min-width: ${breakpoints.lg}) {
padding: 32px;
max-width: 1200px;
margin: 0 auto;
}
`;
const variants = {
primary: css`
background: #3b82f6;
color: white;
`,
secondary: css`
background: #e5e7eb;
color: #1f2937;
`,
outline: css`
background: transparent;
border: 2px solid #3b82f6;
color: #3b82f6;
`,
};
const Button = styled.button<{ $variant?: keyof typeof variants }>`
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
${props => variants[props.$variant || 'primary']}
`;
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.