Skip to main content

components

React component architecture for creating composable, accessible components with data attributes. Use when creating/updating composable components, not for higher-level feature/page components.

Informations de source

Dépôt
udecode/plate
Dernière activité de la source
1 juin 2026 à 17:16
Langue détectée de SKILL.md
anglais
Étoiles
16 632
Forks
993

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
description
React component architecture for creating composable, accessible components with data attributes. Use when creating/updating composable components, not for higher-level feature/page components.
name
components
metadata
{"skiller":{"source":".agents/rules/components.mdc"}}
# Accessibility URL: /accessibility title: Accessibility description: Building components that are usable by everyone, including users with disabilities who rely on assistive technologies. Accessibility (a11y) is not an optional feature—it's a fundamental requirement for modern web components. Every component must be usable by everyone, including people with visual, motor, auditory, or cognitive disabilities. This guide is a non-exhaustive list of accessibility principles and patterns that you should follow when building components. It's not a comprehensive guide, but it should give you a sense of the types of issues you should be aware of. If you use a linter with strong accessibility rules like [Ultracite](https://www.ultracite.ai), these types of issues will likely be caught automatically, but it's still important to understand the principles. ## Core Principles 1. **Semantic HTML First** - Use native elements (`<button>`, `<nav>`, `<ul>`) for built-in accessibility 2. **Keyboard Navigation** - Support Tab, Arrow keys, Home/End, Escape, Enter/Space for all interactions 3. **Screen Reader Support** - Use ARIA attributes (`aria-label`, `aria-current`, `aria-live`) for proper announcements 4. **Visual Accessibility** - Ensure focus indicators, sufficient contrast (4.5:1), and responsive text sizing ## ARIA Patterns ARIA enhances semantic HTML for assistive technologies. Key rules: 1. Use semantic HTML first, ARIA only when necessary 2. Don't override native semantics 3. All interactive elements need keyboard access and accessible names **Common Attributes:** - **Roles** - Define element type (`role="button"`, `role="navigation"`, `role="alert"`) - **States** - Describe current state (`aria-checked`, `aria-expanded`, `aria-selected`) - **Properties** - Provide context (`aria-label`, `aria-describedby`, `aria-controls`, `aria-required`, `aria-invalid`) ## Component Patterns Complex interactive components require specific accessibility patterns. For detailed implementations, consult [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/). **Modal/Dialog:** - `role="dialog"`, `aria-modal="true"`, `aria-labelledby` - Trap focus with Tab, close with Escape - Store and restore previous focus - Prevent body scroll when open **Dropdown Menu:** - `role="menu"` on container, `role="menuitem"` on items - `aria-haspopup="true"`, `aria-expanded`, `aria-controls` - Arrow keys navigate, Enter/Space select, Escape closes **Tabs:** - `role="tablist"` on container, `role="tab"` on buttons, `role="tabpanel"` on panels - `aria-selected`, `aria-controls`, `aria-labelledby` - Arrow Left/Right navigate, Home/End jump to first/last - Only active tab is focusable (`tabIndex={0/-1}`) **Forms:** - `<label htmlFor>` paired with input `id` - `aria-required`, `aria-invalid`, `aria-describedby` for validation - Error messages with `role="alert"` - Group related inputs with `<fieldset>` and `<legend>` ## Focus Management - **Focus Visible** - Use `:focus-visible` for keyboard-only focus indicators - **Focus Trapping** - Trap Tab/Shift+Tab within modals by cycling between first and last focusable elements - **Focus Restoration** - Store `document.activeElement` before opening overlays, restore on close ## Live Regions Announce dynamic content changes to screen readers: - **Status Messages** - `aria-live="polite"` (waits), `aria-live="assertive"` (interrupts), `role="alert"` for errors - **Progress** - `role="progressbar"` with `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, `aria-label` ## Color and Contrast - **Contrast Ratios** - Normal text: 4.5:1, Large text (≥18pt/14pt bold): 3:1, Non-text (icons, borders): 3:1 - **Color Independence** - Never use color alone; combine with text, icons, or ARIA attributes ## Mobile Accessibility - **Touch Targets** - Minimum 44×44px (iOS) or 48×48dp (Android) - **Viewport** - Allow zoom (`<meta name="viewport" content="width=device-width, initial-scale=1">`) ## Common Pitfalls 1. **Placeholder as Label** - Use persistent `<label>`, not disappearing placeholders 2. **Empty Buttons** - Icon buttons need `aria-label` or visually hidden text 3. **Disabled Elements** - Use `aria-disabled` instead of `disabled` to keep focusability and explain why # asChild URL: /as-child title: asChild description: How to use the `asChild` prop to render a custom element within the component. The `asChild` prop is a powerful pattern in modern React component libraries. Popularized by [Radix UI](https://www.radix-ui.com/primitives/docs/guides/composition) and adopted by [shadcn/ui](https://ui.shadcn.com), this pattern allows you to replace default markup with custom elements while maintaining the component's functionality. ## Understanding `asChild` When `asChild` is `true`, instead of rendering its default DOM element, the component merges its props, behaviors, and event handlers with its immediate child element. ```tsx // Without asChild: Creates wrapper <Dialog.Trigger><button>Open</button></Dialog.Trigger> // Output: <button data-state="closed"><button>Open</button></button> // With asChild: Merges props <Dialog.Trigger asChild><button>Open</button></Dialog.Trigger> // Output: <button data-state="closed">Open</button> ``` ## How It Works Uses `React.cloneElement` to clone the child and merge props (including event handlers) from both parent and child components. The enhanced child is returned with combined functionality. ## Key Benefits 1. **Semantic HTML** - Use the most appropriate element (links for navigation, buttons for actions) 2. **Clean DOM Structure** - Eliminates wrapper elements and "wrapper hell" 3. **Design System Integration** - Works seamlessly with existing component libraries 4. **Component Composition** - Compose multiple behaviors onto a single element ## Common Use Cases - **Custom Triggers** - Replace default triggers with custom components or links - **Accessible Navigation** - Maintain semantic navigation elements - **Form Integration** - Integrate with form libraries while preserving functionality ## Best Practices 1. **Maintain Accessibility** - Ensure child elements have proper semantics and ARIA attributes 2. **Document Support** - Use JSDoc to document the `asChild` prop in your component interfaces 3. **Test Forwarding** - Verify props are properly forwarded to child components 4. **Handle Edge Cases** - Consider conditional rendering and dynamic children ## Common Pitfalls 1. **Not Spreading Props** - Child components must spread `...props` to receive merged behavior 2. **Multiple Children** - `asChild` expects exactly one child element, not multiple 3. **Fragment Children** - Fragments are not valid, use actual HTML elements # Composition URL: /composition title: Composition description: The foundation of building modern UI components. Composition, or composability, is the foundation of building modern UI components. It is one of the most powerful techniques for creating flexible, reusable components that can handle complex requirements without sacrificing API clarity. Instead of cramming all functionality into a single component with dozens of props, composition distributes responsibility across multiple cooperating components. Fernando gave a great talk about this at React Universe Conf 2025, where he shared his approach to rebuilding Slack's Message Composer as a composable component. <Video src="https://www.youtube.com/watch?v=4KvbVq3Eg5w" /> ## Making a component composable To make a component composable, you need to break it down into smaller, more focused components. For example, let's take this Accordion component: ```tsx title="accordion.tsx" import { Accordion } from '@/components/ui/accordion'; const data = [ { title: 'Accordion 1', content: 'Accordion 1 content', }, { title: 'Accordion 2', content: 'Accordion 2 content', }, { title: 'Accordion 3', content: 'Accordion 3 content', }, ]; return <Accordion data={data} />; ``` While this Accordion component might seem simple, it's handling too many responsibilities. It's responsible for rendering the container, trigger and content; as well as handling the accordion state and data. Customizing the styling of this component is difficult because it's tightly coupled. It likely requires global CSS overrides. Additionally, adding new functionality or tweaking the behavior requires modifying the component source code. To solve this, we can break this down into smaller, more focused components. ### 1. Root Component First, let's focus on the container - the component that holds everything together i.e. the trigger and content. This container doesn't need to know about the data, but it does need to keep track of the open state. However, we also want this state to be accessible by child components. So, let's use the Context API to create a context for the open state. Finally, to allow for modification of the `div` element, we'll extend the default HTML attributes. We'll call this component the "Root" component. ```tsx title="@/components/ui/accordion.tsx" type AccordionProps = React.ComponentProps<'div'> & { open: boolean; setOpen: (open: boolean) => void; }; const AccordionContext = createContext<AccordionProps>({ open: false, setOpen: () => {}, }); export type AccordionRootProps = React.ComponentProps<'div'> & { open: boolean; setOpen: (open: boolean) => void; }; export const Root = ({ children, open, setOpen, ...props }: AccordionRootProps) => ( <AccordionContext.Provider value={{ open, setOpen }}> <div {...props}>{children}</div> </AccordionContext.Provider> ); ``` ### 2. Item Component The Item component is the element that contains the accordion item. It is simply a wrapper for each item in the accordion. ```tsx title="@/components/ui/accordion.tsx" export type AccordionItemProps = React.ComponentProps<'div'>; export const Item = (props: AccordionItemProps) => <div {...props} />; ``` ### 3. Trigger Component The Trigger component is the element that opens the accordion when activated. It is responsible for: - Rendering as a button by default (can be customized with `asChild`) - Handling click events to open the accordion - Managing focus when accordion closes - Providing proper ARIA attributes Let's add this component to our Accordion component. ```tsx title="@/components/ui/accordion.tsx" export type AccordionTriggerProps = React.ComponentProps<'button'> & { asChild?: boolean; }; export const Trigger = ({ asChild, ...props }: AccordionTriggerProps) => ( <AccordionContext.Consumer> {({ open, setOpen }) => <button onClick={() => setOpen(!open)} {...props} />} </AccordionContext.Consumer> ); ``` ### 4. Content Component The Content component is the element that contains the accordion content. It is responsible for: - Rendering the content when the accordion is open - Providing proper ARIA attributes Let's add this component to our Accordion component. ```tsx title="@/components/ui/accordion.tsx" export type AccordionContentProps = React.ComponentProps<'div'> & { asChild?: boolean; }; export const Content = ({ asChild, ...props }: AccordionContentProps) => ( <AccordionContext.Consumer>{({ open }) => <div {...props} />}</AccordionContext.Consumer> ); ``` ### 5. Putting it all together Now that we have all the components, we can put them together in our original file. ```tsx title="accordion.tsx" import * as Accordion from '@/components/ui/accordion'; const data = [ { title: 'Accordion 1', content: 'Accordion 1 content', }, { title: 'Accordion 2', content: 'Accordion 2 content', }, { title: 'Accordion 3', content: 'Accordion 3 content', }, ]; return ( <Accordion.Root open={false} setOpen={() => {}}> {data.map((item) => ( <Accordion.Item key={item.title}> <Accordion.Trigger>{item.title}</Accordion.Trigger> <Accordion.Content>{item.content}</Accordion.Content> </Accordion.Item> ))} </Accordion.Root> ); ``` ## Naming Conventions When building composable components, consistent naming conventions are crucial for creating intuitive and predictable APIs. Both shadcn/ui and Radix UI follow established patterns that have become the de facto standard in the React ecosystem. ### Root Components The `Root` component serves as the main container that wraps all other sub-components. It typically manages shared state and context by providing a context to all child components. ```tsx <AccordionRoot>{/* Child components */}</AccordionRoot> ``` ### Interactive Elements Interactive components that trigger actions or toggle states use descriptive names: - `Trigger` - The element that initiates an action (opening, closing, toggling) - `Content` - The element that contains the main content being shown/hidden ```tsx <CollapsibleTrigger>Click to expand</CollapsibleTrigger> <CollapsibleContent> Hidden content revealed here </CollapsibleContent> ``` ### Content Structure For components with structured content areas, use semantic names that describe their purpose: - `Header` - Top section containing titles or controls - `Body` - Main content area - `Footer` - Bottom section for actions or metadata ```tsx <DialogHeader> {/* Form title */} </DialogHeader> <DialogBody> {/* Form content */} </DialogBody> <DialogFooter> {/* Form footer */} </DialogFooter> ``` ### Informational Components Components that provide information or context use descriptive suffixes: - `Title` - Primary heading or label - `Description` - Supporting text or explanatory content ```tsx <CardTitle>Project Statistics</CardTitle> <CardDescription> View your project's performance over time </CardDescription> ``` # Data Attributes URL: /data-attributes title: Data Attributes description: Add data attributes to expose component state and enable flexible styling. Data attributes provide a way to expose component state and structure to consumers for styling. Use two patterns: `data-state` for visual states and `data-slot` for component identification. ## When Creating Components **Add `data-state` attributes** to expose component state: - Visual states (open/closed, active/inactive, loading) - Layout states (orientation, side, alignment) - Interaction states (disabled, hover, focus when styling children) **Add `data-slot` attributes** for stable component identification: - Use kebab-case naming (`data-slot="submit-button"`) - Name reflects purpose, not implementation - Provides stable selectors that won't break when internals change ## Decision Framework When creating a component, choose the appropriate API:
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub