@takeoff-ui/react-spar
v0.1.2
Published
React-first Takeoff wrappers built on top of @turkish-technology/spar.
Maintainers
Readme
@takeoff-ui/react-spar
Current-phase React package for Takeoff components backed by
@turkish-technology/spar. Components ship as compound surfaces: state lives on
the root and structure lives in named subcomponents.
The public API uses React primitive vocabulary for Accordion state (value,
defaultValue, onValueChange, item value) while preserving visual Takeoff
vocabulary (type, mode, size) and translating framework mechanics into
React conventions (default* props, on* callbacks, and compound children
instead of Web Component slots).
Current Surface
The package currently exports:
AccordionButtonDrawerTooltipTakeoffSparProvider- customization and theme types from the package root
See the docs site for components queued next. Additional wrappers are added only after their component contract is source-backed and any upstream Spar behavior gaps are resolved.
Reference
- Spar documentation: https://spar.app.turkishtechlab.com/
- Spar Accordion reference: https://spar.app.turkishtechlab.com/docs/Components/Accordion
Install
@takeoff-ui/react-spar currently targets React 19.x only.
@takeoff-design/tokens is a peer dependency — install it alongside
@takeoff-ui/react-spar. Keeping tokens out of dependencies ensures a single
tokens copy in your tree and lets you upgrade tokens independently of this
wrapper.
pnpm add @takeoff-ui/react-spar @takeoff-design/tokens@turkish-technology/spar is still a regular dependency of
@takeoff-ui/react-spar and is installed automatically. Add it to your own
package.json only if your app imports from it directly (for example, when
using Spar primitives that react-spar does not re-export).
@takeoff-ui/react-spar does not bundle component CSS. Import the token
stylesheet once at the app shell or entrypoint:
import '@takeoff-design/tokens/css/default/theme.css';Usage
import '@takeoff-design/tokens/css/default/theme.css';
import { Accordion, TakeoffSparProvider } from '@takeoff-ui/react-spar';
export function Example() {
return (
<TakeoffSparProvider>
<Accordion defaultValue="baggage">
<Accordion.Item value="baggage">
<Accordion.Header>
<Accordion.Trigger>Baggage allowance</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
Review your cabin and checked baggage limits before your trip.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="changes">
<Accordion.Header>
<Accordion.Trigger>Flight changes</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
Change rules depend on the fare family selected during booking.
</Accordion.Content>
</Accordion.Item>
</Accordion>
</TakeoffSparProvider>
);
}TakeoffSparProvider accepts colorMode ('light' | 'dark', default
'light'), an optional locale string, and an optional components
customization map. The provider renders a display: contents wrapper that
writes data-theme from colorMode and lang from locale.
Accordion
<Accordion multiple defaultValue={['one']}>
<Accordion.Item value="one">
<Accordion.Header>
<Accordion.Trigger>
FAQ
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>Answer content</Accordion.Content>
</Accordion.Item>
</Accordion>- Root behavior props:
value,defaultValue,multiple,onValueChange,collapsible,disabled,orientation. - Root visual props:
type,mode,size. - Trigger leading content:
startContentprop onAccordion.Trigger. - Item props: required
value, optionaldisabled. - Public parts:
Accordion.Item,Accordion.Header,Accordion.Trigger,Accordion.Indicator,Accordion.Content. - The disclosure indicator is opt-in: drop
<Accordion.Indicator />into the trigger to render the default chevron, override its children to swap glyphs, or omit it to ship a trigger without a visual affordance. Placement (left vs right of the title) follows where you put it inside the trigger. - Web Component shortcuts such as item-level
active,header, and custom DOM active-index events are intentionally not part of the React surface; use root state props and compound children instead.
Customization
Every public component part exposes the same customization layers:
className: appended to the canonical root slot class.classNames: per-slot extra classes, concatenated with canonicaltk-*classes.slotProps: per-slot HTML attributes, shallow-merged below canonical wrapper attributes.- provider
components: global defaults, classes, and slot props keyed by component name.
Canonical tk-* classes and data-slot attributes are always preserved.
Theme-level Defaults
<TakeoffSparProvider
components={{
Accordion: {
defaultProps: { size: 'large' },
className: 'travel-faq',
},
AccordionTrigger: {
slotProps: { root: { 'aria-describedby': 'faq-trigger-hint' } },
},
}}
>
{children}
</TakeoffSparProvider>Instance props override provider defaults. Instance classNames and slotProps
override provider entries for the same slot, while canonical wrapper attributes
remain in place.
Per-instance Classes
<Accordion.Item value="faq" classNames={{ root: 'faq-item' }}>
<Accordion.Header>
<Accordion.Trigger classNames={{ root: 'faq-trigger' }}>
FAQ
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>Answer text</Accordion.Content>
</Accordion.Item>Per-instance Slot Props
<Accordion.Item
value="faq"
slotProps={{
root: { 'aria-describedby': 'faq-note' },
}}
>
<Accordion.Header>
<Accordion.Trigger>FAQ</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>Answer text</Accordion.Content>
</Accordion.Item>Custom Indicator
<Accordion>
<Accordion.Item value="faq">
<Accordion.Header>
<Accordion.Trigger>
FAQ
<Accordion.Indicator>
{({ isOpen }) => (isOpen ? <span>-</span> : <span>+</span>)}
</Accordion.Indicator>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>Answer text</Accordion.Content>
</Accordion.Item>
</Accordion>Custom arrow content is rendered inside the canonical .tk-accordion-item-arrow
owner node so recipes keep their stable selector.
Accordion.Indicator is opt-in — omit it to render a trigger without a
disclosure affordance. Its owner node carries the canonical
.tk-accordion-item-indicator class so recipes keep a stable selector.
