.claude/skills/context-separation/SKILL.md
Context separation (맥락 분리) — recursive decomposition of mixed concerns into single-context units. Covers execution path splitting, concept limits, hook isolation, discriminated unions, props drilling, providers, variants, React 19. Used in Phase 1 of /refactor (recursive structural changes).
npx skillsauth add taewoongheo/taste context-separationInstall 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.
10 rules. Apply recursively: separate → verify → separate again until each unit has a single context.
When code paths are mutually exclusive (e.g., role-based rendering), split them into separate components.
// BAD — viewer and admin logic interleaved
function SubmitButton() {
const isViewer = useRole() === "viewer";
useEffect(() => {
if (isViewer) return;
showButtonAnimation();
}, [isViewer]);
return isViewer ? <TextButton disabled>Submit</TextButton> : <Button type="submit">Submit</Button>;
}
// GOOD — split by execution path
function SubmitButton() {
const isViewer = useRole() === "viewer";
return isViewer ? <ViewerSubmitButton /> : <AdminSubmitButton />;
}
Humans can hold ~6-7 items in working memory. If a component or hook manages more, extract into well-named abstractions. Count distinct concerns, not lines.
Techniques:
Don't bundle unrelated states or API calls into one mega-hook. Each hook should manage a single concern.
// BAD — one hook manages all query params
export function usePageState() {
const [query, setQuery] = useQueryParams({ cardId: NumberParam, dateFrom: DateParam });
return { values: { /* all */ }, controls: { /* all setters */ } };
}
// GOOD — one hook per concern
export function useCardIdQueryParam() {
const [cardId, setCardId] = useQueryParam("cardId", NumberParam);
return [cardId ?? undefined, setCardId] as const;
}
When multiple boolean states represent the same concept (e.g., request status), impossible combinations can occur. Use a discriminated union to make invalid states unrepresentable.
// BAD — isLoading && isError is possible but makes no sense
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
// GOOD — only one status at a time
const [status, setStatus] = useState<'idle' | 'loading' | 'error' | 'success'>('idle');
When to refactor: 2+ boolean states that are mutually exclusive (only one should be true at a time). Replace with a single discriminated union.
Does NOT apply to independent boolean props for conditional rendering (e.g., isVisible, isEditable controlling different concerns).
When a component forwards 5+ props to children without using them, it's a delivery proxy — not a real component.
Detection: Count props that a component receives but only passes through to children without reading or transforming.
// BAD — ItemEditBody forwards onConfirm, items, recommendedItems without using them
<ItemEditBody items={items} recommendedItems={recommendedItems} onConfirm={onConfirm} onClose={onClose}>
// GOOD — composition: each component receives only what it uses
<ItemEditBody keyword={keyword} onKeywordChange={setKeyword} onClose={onClose}>
<ItemEditList keyword={keyword} items={items} recommendedItems={recommendedItems} onConfirm={onConfirm} />
</ItemEditBody>
For complex cases with many composable parts, use the compound component pattern:
<Select onValueChange={handleSelect}>
<Select.Trigger>
<Select.Value placeholder="Choose..." />
</Select.Trigger>
<Select.Content>
{options.map((o) => (
<Select.Item key={o.value} value={o.value} label={o.label} />
))}
</Select.Content>
</Select>
When to refactor: A component forwards 5+ props it doesn't use. Consider children composition first, compound components for complex cases.
Decouple context consumers from implementation by using an interface.
// BAD — consumers coupled to Zustand internals
const StoreContext = createContext<ReturnType<typeof useZustandStore>>(null);
// GOOD — interface-based
interface AuthContext {
user: User | null;
signIn: (credentials: Credentials) => Promise<void>;
signOut: () => Promise<void>;
}
const AuthContext = createContext<AuthContext | null>(null);
When multiple siblings need shared state, lift it into a provider rather than passing through a common parent.
// BAD — parent manages state for all children
function Parent() {
const [selected, setSelected] = useState<string | null>(null);
return (
<>
<Sidebar selected={selected} onSelect={setSelected} />
<Content selected={selected} />
</>
);
}
// GOOD — provider holds state, children subscribe independently
function SelectionProvider({ children }: { children: ReactNode }) {
const [selected, setSelected] = useState<string | null>(null);
return (
<SelectionContext value={{ selected, setSelected }}>
{children}
</SelectionContext>
);
}
children is simpler and more composable than render props for most cases.
// BAD — render prop
<Card renderHeader={() => <Text>Title</Text>} renderBody={() => <Text>Body</Text>} />
// GOOD — children composition
<Card>
<Card.Header><Text>Title</Text></Card.Header>
<Card.Body><Text>Body</Text></Card.Body>
</Card>
Use render props only when the child needs data from the parent that isn't available via context.
Instead of conditional logic inside one component, create named variants.
// BAD — one component with branching
function Alert({ type }: { type: 'info' | 'error' | 'success' }) {
const color = type === 'error' ? 'red' : type === 'success' ? 'green' : 'blue';
const icon = type === 'error' ? '!' : type === 'success' ? '✓' : 'i';
return <View style={{ backgroundColor: color }}><Text>{icon}</Text></View>;
}
// GOOD — shared base, explicit variants
function AlertBase({ color, icon, children }: AlertBaseProps) {
return <View style={{ backgroundColor: color }}><Text>{icon}</Text>{children}</View>;
}
function ErrorAlert({ children }: { children: ReactNode }) {
return <AlertBase color="red" icon="!">{children}</AlertBase>;
}
forwardRef. Pass ref directly as a prop.use() instead of useContext(): use(Context) works inside conditionals and loops.// OLD — React 18
const Input = forwardRef<TextInput, InputProps>((props, ref) => {
const theme = useContext(ThemeContext);
return <TextInput ref={ref} style={{ color: theme.text }} {...props} />;
});
// NEW — React 19
function Input({ ref, ...props }: InputProps & { ref?: Ref<TextInput> }) {
const theme = use(ThemeContext);
return <TextInput ref={ref} style={{ color: theme.text }} {...props} />;
}
| # | Rule | Impact | One-liner |
|---|------|--------|-----------|
| CS-1 | Separate non-co-running code | HIGH | Split by execution path |
| CS-2 | ≤ 6-7 concepts per unit | HIGH | Extract components/hooks when over limit |
| CS-3 | One concern per hook | MEDIUM | Don't bundle unrelated state |
| CS-4 | Discriminated unions | CRITICAL | Mutually exclusive states → single union type |
| CS-5 | Resolve props drilling | HIGH | 5+ forwarded unused props → composition or compound |
| CS-6 | Context interfaces | HIGH | Interface over implementation for contexts |
| CS-7 | Lift state to providers | HIGH | Shared state → context provider |
| CS-8 | Children over render props | MEDIUM | children > renderX for most cases |
| CS-9 | Explicit variants | MEDIUM | Named variants over conditional branching |
| CS-10 | React 19 APIs | MEDIUM | No forwardRef, use() over useContext() |
development
Visual hierarchy verification — squint test, information density, primary action clarity, and empty state quality for mobile UI. Auto-load when doing design or layout work.
development
Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types.
development
TDD patterns for React Native — Jest setup, React Native Testing Library patterns, mock strategies, and what to test vs skip. Auto-load when writing or running tests.
testing
Spacing, layout, and spatial relationships — consistent scale, proximity grouping, optical alignment, and density control for mobile UI. Auto-load when doing design or layout work.