Build new Trioxygen components. Use when adding a component to the design system, wrapping React Aria primitives, or writing component showcase pages. Enforces React 19 standards, Vanilla Extract styling, and compound component patterns.
Use this skill when creating or modifying components in the Trioxygen design system.
forwardRef)React 19 passes ref directly as a prop. Never use forwardRef.
// ✅ React 19
export function Input({ ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />
}
// ❌ Old pattern — do not use
export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => { ... })
Always use top-level import type for types, never inline import { type Foo }.
// ✅
import type React from 'react'
import type { ButtonProps } from 'react-aria-components'
import { Button } from 'react-aria-components'
// ❌
import { Button, type ButtonProps } from 'react-aria-components'
use()Prefer use(Context) over useContext(Context) for reading context in React 19.
// ✅ React 19
import { use } from 'react'
const value = use(MyContext)
// Also fine for throwing errors on missing context
For async user interactions, use useTransition + async action functions or the new useActionState:
const [state, action, isPending] = useActionState(async (prev, formData) => {
await submitForm(formData)
return { success: true }
}, null)
useEffect for derived stateCompute derived values inline. Only use useEffect for true side-effects (subscriptions, DOM mutations).
Every interactive element must use a RAC primitive as its root:
Button from react-aria-componentsTextField + InputSelect + ListBox + PopoverModalOverlay + Modal + DialogDisclosureGroup + Disclosure + DisclosurePanelLink (for <a> tags in nav)Never use :hover, :focus, :active in CSS. Use RAC data attributes:
// ✅ In *.css.ts