Skip to main content

Changelog

Release notes

A quiet log of what shipped in react-spar. Newest first. Each entry is a release beat — what landed, why it matters, and where to look.

The package gets a test suite, a coverage gate, and the six fixes it surfaced

Every component, core helper and hook is now covered by tests — 517 to 2414 of them — and CI runs them behind coverage thresholds, so a component can no longer land untested. Writing those tests surfaced six bugs, each fixed here: provider `slotProps` overriding instance props, `Select` never marking itself invalid, `Dialog` and `Drawer` ignoring `dismissible={false}` for Escape, `Table` utility columns unreachable by assistive technology, and `useDatePicker` keeping a stale `Date`.

Fixes

  • **Provider layering** — a theme slotProps.root value replaced an attribute or handler the instance set itself; a title or onClick passed to a component lost to the theme’s. Fixed once in buildSlotAttrs, which also restores the documented class order (canonical, provider, instance) and stops dropping a className passed through slotProps.
  • **Select** — invalid never reached Spar, so <Select invalid> rendered no data-invalid on the root and no aria-invalid on the trigger. The standalone Select.Indicator read the open state under a name Spar’s context does not use, so its chevron never flipped.
  • **Dialog / Drawer** — dismissible={false} did not block Escape, and a consumer’s own onEscapeKeyDown veto was ignored. Closing also returns focus to the trigger again. Both were Spar bugs, fixed upstream in @turkish-technology/spar@0.2.3, which this release pins.
  • **Table accessibility** — the single-mode selection column and the expansion column had empty headers, so every such table failed axe’s empty-table-header rule. Single-mode row radios had no accessible name, and the “Go to page” submit was an Input.TrailingIcon, which hides itself from assistive technology.
  • **useDatePicker** — a filled date edited back to half-typed left the value at undefined without calling onValueChange, so a bound form kept submitting the stale Date.
Testing and CI
  • The 16 components that had no tests now have colocated suites, and the thin ones (input, dialog, dropdown, switch, checkbox, label, table) were deepened. core, the hooks, the provider and the slot registry are tested directly.
  • Coverage went from 76.3% to 99.55% of statements and 75.6% to 97.59% of branches. The old numbers flattered the package: components with no tests of their own showed high coverage only because other components rendered them.
  • CI runs react-spar through test:coverage and the tokens suite before the build. The unit-test step had been dropped when the package had no tests and never came back.
  • vitest.config.ts gains v8 coverage with thresholds just under the measured totals, so a component that lands without tests fails the job.
  • Every suite was mutation-checked against its source: 68 mutants, 66 killed.
Package details1 package
@takeoff-ui/react-spar0.5.1
Patch Changes
  • [#229](https://github.com/turkishtechnology/takeoff-v2/pull/229)

[efa25ad](https://github.com/turkishtechnology/takeoff-v2/commit/efa25ad8d64a53506288672b24a70ff1a2b08640)

Thanks [@harun-demir](https://github.com/harun-demir)! - Provider slotProps

no longer override what an instance sets itself, and classes land in the

documented order.

The provider's layers are documented to sit below the instance, but a theme

slotProps.root value replaced the instance's own attribute or handler of the

same name: a title or onClick passed to the component lost to the theme's.

Every component root spreads its props before the composed attrs, so this is

fixed once in buildSlotAttrs. A theme key the instance sets is left out, a

theme style merges key by key under the instance style, and classes still

add up.

Two class bugs on the same path are fixed with it. The instance className

now follows the provider's classes (canonical, provider, instance, as

documented), and a className passed through slotProps is added instead of

silently dropped.

  • [#229](https://github.com/turkishtechnology/takeoff-v2/pull/229)

[31886fe](https://github.com/turkishtechnology/takeoff-v2/commit/31886fe8e20e00dc6ca19804ed25e6400b06de78)

Thanks [@harun-demir](https://github.com/harun-demir)! - Select now marks

itself invalid, and Select.Indicator follows the open state.

<Select invalid> set data-invalid on the trigger and nowhere else. The

prop never reached Spar, whose root writes data-invalid after the props it

spreads, so the root lost the attribute and the trigger never got

aria-invalid. It is now passed through as given, so an unset invalid still

falls back to a wrapping Field.

The standalone Select.Indicator read the open state under a name Spar's

context does not use. Its default chevron never flipped, and render-function

children always received { isOpen: undefined }. Both now track the list.

  • [#229](https://github.com/turkishtechnology/takeoff-v2/pull/229)

[4bd6bb2](https://github.com/turkishtechnology/takeoff-v2/commit/4bd6bb22cd93892bca22d779dd6eff898f4225bb)

Thanks [@harun-demir](https://github.com/harun-demir)! - Dialog and Drawer

honour dismissible={false} for Escape, and closing returns focus to the

trigger.

Both were Spar bugs, fixed upstream in @turkish-technology/spar@0.2.3, which

this release pins.

Dialog.Panel and Drawer.Panel already blocked the dismissal by calling

preventDefault() on the event Spar hands to onEscapeKeyDown, but Spar read

the veto from the React event, which never sees a preventDefault() made on

the native one. Escape therefore closed a non-dismissible dialog or drawer,

and a consumer's own onEscapeKeyDown veto was ignored too. Spar also cleared

the stored trigger before its root could restore focus, so restoreFocus (on

by default) never moved focus back on close.

  • [#229](https://github.com/turkishtechnology/takeoff-v2/pull/229)

[ba34aa5](https://github.com/turkishtechnology/takeoff-v2/commit/ba34aa58e22f5beb9f14b9887b0385cf665f6d9f)

Thanks [@harun-demir](https://github.com/harun-demir)! - Table utility

columns and the page jump are now reachable by assistive technology.

  • The selection column in single mode and the expansion column had empty

header cells, so every such table failed axe's empty-table-header rule. Both

now carry a visually hidden name: "Row selection" and "Row details".

  • In single selection mode the row radios had no accessible name, because the

label sat on each one-item radio group instead of the radio. It is now on

the radio, which also renders as a span so Spar's <label role="radio"> no

longer trips aria-allowed-role.

  • The "Go to page" submit was an Input.TrailingIcon, which hides itself from

assistive technology, so a focusable button had no accessible presence. It

is now a text Button in the input's tk-input-action hook.

Calendar and the date picker composition, plus Input masking

`Calendar` lands as the catalog’s first date component: a month grid over `react-day-picker@10` where the wrapper owns everything visual, and the grid now follows a value set from outside it. Alongside it, `useDatePicker` bridges a masked text field and a `Date` for the Popover + Calendar composition — it owns no anatomy, so every element stays yours to place. `Input.Field` gains masking, and `Drawer` opens up `modal`, `forceMount`, and the panel `role`.

Highlights

  • **Calendar** — a month grid over react-day-picker@10, speaking Takeoff vocabulary: mode (single | range | multiple), minDate / maxDate, disabledDates, allowedDates, disabledWeekDays, and size. headerType adds month and year boards to the caption, and renderDay customizes day contents while the engine keeps the day-button behavior. No rdp-* class reaches the DOM and no library stylesheet is imported.
  • **The grid follows a value set from outside it.** Previously the engine owned the displayed month after first render, so a preset button or a value restored from a form could select a day that was off-screen. Setting the value is now enough — a passed month still wins outright, and navigation the user performs is never undone.
  • **useDatePicker** — the text/Date bridge for the Popover + Calendar composition. It renders nothing and owns no anatomy: inputProps, calendarProps, and popoverProps are ordinary objects you spread, override one key of, or ignore.
  • **Input.Field masking** — mask takes a shape (blocks, delimiters, numericOnly), a regex, one of the date / time / number presets, or a custom resolver. onValueChange(value, meta) reports every edit with raw, completed, and iso — including delimiter-aware deletes and undo, which never surface as a change event.
  • **Drawer** — modal={false} leaves the page live behind the panel, forceMount matches what Dialog already offered, and Drawer.Panel accepts role for an interrupting drawer.
Fixes
  • Calendar header and grid no longer shift: the caption label, the day grid, and the card each sit in a pinned box, so a long month name cannot resize the card and paging months cannot resize the grid.
  • The Calendar card is wider than the grid, so the year in the caption stops ellipsizing, and the nav is sized on its own rather than on the day cell.
  • Arrow keys on the month and year boards are mirrored for RTL, with minDate / maxDate and disabled dates covered.
  • The range-end corner is guarded the way range-start already was.
  • The auto margin between an Input action and a picker trigger is cancelled, so the action group sits flush.
  • Icon gallery: tiles are centred, labels cut to one line, and the drawer height is pinned.
Docs
  • A datepicker page documenting the Popover + Calendar composition, and the contract behind it.
  • Binding a date picker to a form, with a live field in both form demos.
  • Localization for Calendar and the date picker composition.
  • Input masking: the contract, the data attributes Spar emits, and what a resolver’s omitted meta fields default to.
  • Agent skills for the datepicker composition and for masking.
Infrastructure
  • The @turkish-technology/spar dependency was updated.
  • Mask tests are named after the component under test.
  • The Claude PR review workflow was removed.
Package details3 packages
@takeoff-ui/react-spar0.5.0
Minor Changes
  • [#206](https://github.com/turkishtechnology/takeoff-v2/pull/206)

[f8b94f3](https://github.com/turkishtechnology/takeoff-v2/commit/f8b94f385b03fbc9d390fbbe6c39da57e0c3a4a9)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - New

component: Calendar.

A month grid over react-day-picker@10. Spar ships no date primitive, so —

like Table over TanStack — the engine here is third-party and the wrapper

owns everything visual: a complete classNames map replaces the library's

own, so no rdp-* class reaches the DOM and no library stylesheet is

imported. data-slot anchors and slotProps reach the engine's internal

nodes through a components override map whose component identities are

stable, so the grid is not remounted between renders. Date arithmetic,

selection, keyboard navigation and ARIA stay upstream.

Its API speaks Takeoff Core's vocabulary rather than the engine's: mode

(single | range | multiple), minDate / maxDate, disabledDates,

allowedDates, disabledWeekDays, firstDayOfWeekIndex and size, each

mapped to the engine's Matcher / navigation props by a pure helper. value

is Date-based rather than Core's string: parsing and formatting a

dateFormat grammar in the wrapper would be behavior, not visual wrapping.

locale takes an object from react-day-picker/locale, since resolving a

locale string would mean bundling every locale.

Day contents can be customized with renderDay, which receives the date and

active modifiers while leaving the engine-owned day-button behavior intact.

The grid follows a value set from outside it. The engine reads defaultMonth

once and then owns the displayed month, so a preset button, a typed date or a

value restored from a form would select a day that is off-screen — every

caller had to hold a second piece of state and keep setValue and setMonth

in step. Setting the value is now enough. It is deliberately narrow: a passed

month wins outright (that parent is driving the grid), and only a selection

landing in a different month than the one on screen moves it, so navigation

the user performs is never undone.

```tsx

const [date, setDate] = useState<Date>();

<Calendar value={date} onValueChange={setDate} minDate={new Date()} />;

`

@takeoff-design/tokens ships the tk-calendar-* recipe. It is built on the

Figma datepicker.* token family — the one that describes a picking grid

(-items-* day cells, -header-* month row, -body-* grid, -footer-*) —

so day size, cell radius and every inset come from the design system rather

than from local values. The separate calendar.* family is left alone:

calendar-activity-* / calendar-week-cell-* belong to the activity

calendar, which is a different component.

That family is also why size has two values rather than three:

datepicker-items-base-size and -small-size are the only grid scales the

design defines — an input row has a large, a month grid does not.

Controlled-ness is decided by whether value is **passed**, not by whether it

currently holds a date — a picker's controlled value is undefined until

something is picked, so reading the value would make the ordinary

const [date, setDate] = useState<Date>() call site silently uncontrolled and

drop every parent-driven change after mount (a preset button, a reset, a saved

value arriving late).

Cells keep their own size rather than stretching: the grid is exactly as wide

as its seven columns, each one datepicker-items-*-size wide, with the

exported row gap between rows.

Day state is read from the attributes the engine already emits, with one

subtlety the design forced: in range mode the engine marks _every_ day of the

span data-selected, so the filled pill is scoped to the two ends and the

middle keeps the light band. The band itself is painted on a pseudo-element

rather than the cell, because border-radius is ignored on a table cell under

border-collapse: collapse — the layout the flush-column design needs.

Two Core header features have no react-day-picker counterpart, so the

wrapper supplies them. headerType (basic | divided | light | primary

| dark) is Core's tk-datepicker header vocabulary: basic divides the

month row from the grid, the rest drop that divider for a boxed surface, and

the two filled ones flip the label and arrows to white. The header also

carries Core's second pair of arrows: each pair steps one rung of the board it

is on — the single ones move a month, or a year on the year board; the double

ones move a year, or a whole twelve-year page.

view (day | month | year) is Core's view switch: the month and the

year in the caption are buttons that swap the day grid for a twelve-month or

twelve-year board. Which board shows is a third controlled pair — view +

onViewChange, with defaultView for the uncontrolled case, alongside the

ones for the selection and the displayed month. The boards are the one part of

the anatomy the engine does not render, so they are wrapper-owned —

role="grid" with one tab stop, arrow-key roving focus, aria-selected on

the current cell, focus moving into a board as it opens and back to the

trigger once a month is picked. Their accessible names come from the engine's

own labels.labelMonthDropdown / labelYearDropdown, so translating the

calendar translates the boards. Only the body is replaced: the displayed month

stays the engine's, through goToMonth. A dropdown* caption keeps the

engine's <select> pair instead of switch buttons and drops the year arrows,

since the year <select> covers them; the boards still work there.

One consequence worth calling out: since a board can replace the day grid on

any calendar, the body box is pinned on both — the day grid carries the same

min-height the month/year board does, so a four- or five-week month keeps

the six-week box instead of making the card shorter. Switching views does not

resize the card, paging months does not either, and fixedWeeks is not needed

to stop the month-to-month jump.

The card's width comes from the grid rather than the caption. The caption is

the month label plus a reserve for the arrows laid over it —

nav-button * 2 + h-padding at each end — so a label wider than what is left

over stretched the card, which then shrank again on the next arrow. The whole

card twitched as you paged, and in a popover the panel's edges moved with it.

A single-month calendar is therefore pinned, the caption takes that width

instead of setting it (min-width: 0, so its nowrap label is not its

automatic minimum), and a label long enough to reach the arrows ellipsizes. A

numberOfMonths > 1 row, legitimately wider than one grid, is left alone.

The card is pinned a little wider than the grid, through its own

--tk-calendar-frame-width (--tk-calendar-body-width + 24px, 336px at

base). The grid cannot absorb that difference itself — its width is divided

between seven table-layout: fixed columns, so widening it would move the day

cells off their exported size — so the grid keeps --tk-calendar-body-width

and centres inside the frame. Both scales take the same 24px; more would leave

the grid rattling in the card.

The navigation is sized on its own rather than on the day cell, through

--tk-calendar-nav-button-size (28px) and --tk-calendar-nav-icon-size

(20px). The two only ever shared a value by coincidence: 40px is a comfortable

day target and a needlessly wide arrow, and the header paid for it twice,

since the caption reserves nav-button * 2 + h-padding at each end. Four 40px

arrows took 192px of a 336px card and left the label 144px; at 28px the

reserve is 112px and the label gets 224px, which is what stopped a four-digit

year disappearing behind the ellipsis on a long month. The day grid is

untouched — cell-size still sizes the cells, the weekday row and the

week-number column.

Inside that caption the label takes the whole space between the arrows

(flex: 1) rather than sizing to its text. Otherwise the header stayed

restless even with the card pinned: "September 2026" and "May 2026" are

different widths, so the month and year buttons — and their hover boxes —

shifted on every arrow click. The box now stays put and only the text inside

it changes.

The same width reaches the two boxes between the card and the caption. The nav

is positioned against .tk-calendar-months, not against the card, and neither

that element nor the month inside it had a width of its own — both sized to

their content, so the month name still decided how wide they were. The card

centres them, so a narrower month moved both edges inward and the arrows,

inset from those edges, drifted with them. All four boxes now share

--tk-calendar-body-width, and a numberOfMonths > 1 row is exempt

throughout.

  • [#196](https://github.com/turkishtechnology/takeoff-v2/pull/196)

[eab6345](https://github.com/turkishtechnology/takeoff-v2/commit/eab634549921dcd37676bc1c0c9b8ac8b29525bc)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Drawer

accepts modal and forceMount, and Drawer.Panel accepts role.

A modal drawer takes the page over: it traps focus, locks body scroll, and

puts a pointer-swallowing overlay between the reader and everything behind the

panel. That is right for a drawer that interrupts — a form, a confirmation —

and wrong for one that inspects something still on screen. Until now modality

was fixed, so the second kind had no way to exist.

modal={false} leaves the page live behind the panel: the reader keeps

scrolling, and a click on the content behind reaches it. Escape and

Drawer.Close still dismiss, and dismissible still governs click-away. Pair

it with omitting Drawer.Overlay — the overlay is what swallows pointer

events, and the scroll lock comes from the root, so both have to go for the

page to stay interactive.

role defaults to 'dialog' and was already exposed by Dialog.Panel; the

drawer's omission was an oversight rather than a decision, so the two panels

now offer the same surface. Pass role="alertdialog" for a drawer that

interrupts and must be acknowledged.

forceMount is exposed too, defaulting to true. The root turns it on so the

panel outlives the open -> closed boundary and the slide-out can run; passing

false unmounts on close and trades that animation away, which is worth it

only for a panel heavy enough to be worth the swap. Dialog already offered

exactly this opt-out — the drawer pinning it was an inconsistency, not a

decision.

All three are picked from Spar's dialog types alongside the rest of the root's

state.

Existing drawers are untouched: the defaults are the behaviour they already

had.

  • [#206](https://github.com/turkishtechnology/takeoff-v2/pull/206)

[c359c7f](https://github.com/turkishtechnology/takeoff-v2/commit/c359c7f3a55531d83662aac9677ea858dda7ef22)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

Input.Field accepts mask and onValueChange.

Masking is a Spar capability, and this exposes it rather than reimplementing

it: mask and onValueChange are picked straight from Spar's field types, so

the contract a consumer writes against is the one Spar validates. Formatting

used to be something each app wired into onChange by hand, which meant every

app also inherited the caret bugs that come with rewriting a controlled

input's value.

A mask is one of four things. A shape lays characters into blocks and puts a

delimiter between them

({ blocks: [4, 4, 4, 4], delimiter: ' ', numericOnly: true }). The date /

time / number presets additionally interpret the value — clamping a month

to 12, a minute to 59, or regrouping an integer as it grows. A regex is

matched one character at a time, so a pattern written for the final value also

accepts every legal prefix. Anything else is a resolver function.

number derives its separators and group sizes from Intl.NumberFormat, so

numberLocale: 'tr-TR' gives 1.234.567,89 and 'en-IN' gives lakh grouping

without a second option, and meta.iso is always a Number()-parseable

string.

Prefer onValueChange(value, meta) over onChange on a masked field: Spar

applies deletions and undo/redo directly to the control, so those edits never

surface as a React change event. meta carries raw, completed and — when

the mask defines a canonical form — iso.

createDateMask, createTimeMask, createNumberMask and the Mask* types

are re-exported from the package, so a resolver that wraps a built-in is an

ordinary typed function. The presets are themselves resolvers, so a built-in

has no capability your own mask lacks.

Omitting mask leaves the field exactly as it was.

  • [#206](https://github.com/turkishtechnology/takeoff-v2/pull/206)

[500cbcb](https://github.com/turkishtechnology/takeoff-v2/commit/500cbcba46c96cdb81f6cf5542539f9a41667233)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

useDatePicker — the text/Date bridge for the date picker composition.

There is still no DatePicker component: a date picker is Popover +

Calendar, composed by the consumer. This hook owns no anatomy and renders

nothing, so every element stays the consumer's to place. What it removes is

the one part of the composition that was neither short nor situational —

keeping a typed string and a Date in step in both directions, and deriving

the mask bounds from the same pair of dates the grid gets, so a date the

calendar rejects cannot be typed either.

```tsx

const picker = useDatePicker({ min: MIN, max: MAX });

<Popover {...picker.popoverProps}>

<Input>

<Input.Field placeholder="dd/mm/yyyy" {...picker.inputProps} />

<Popover.Trigger classNames={{ root: 'tk-input-action' }}>

…

</Popover.Trigger>

</Input>

<Popover.Content classNames={{ root: 'tk-datepicker-panel' }}>

<Calendar {...picker.calendarProps} />

</Popover.Content>

</Popover>;

`

Each group is an ordinary object a caller may spread, override one key of, or

ignore. setValue drives the whole picker from outside — a preset button, a

reset, a value restored from a form — and format / delimiter cover the

field's shape without a formatter wired into onChange.

The displayed month is deliberately not part of it: Calendar now follows a

value set from outside the grid, so a third piece of state would be a second

writer to a month the grid already moves.

It is the only hook the package publishes. createSafeContext,

useControllableState and useContentWidthStyle stay internal — they are

authoring tools for components in this package, where useDatePicker exists

precisely because this pattern has no component to put it inside.

@takeoff-design/tokens0.5.0
Minor Changes
  • [#206](https://github.com/turkishtechnology/takeoff-v2/pull/206)

[f8b94f3](https://github.com/turkishtechnology/takeoff-v2/commit/f8b94f385b03fbc9d390fbbe6c39da57e0c3a4a9)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - New

component: Calendar.

A month grid over react-day-picker@10. Spar ships no date primitive, so —

like Table over TanStack — the engine here is third-party and the wrapper

owns everything visual: a complete classNames map replaces the library's

own, so no rdp-* class reaches the DOM and no library stylesheet is

imported. data-slot anchors and slotProps reach the engine's internal

nodes through a components override map whose component identities are

stable, so the grid is not remounted between renders. Date arithmetic,

selection, keyboard navigation and ARIA stay upstream.

Its API speaks Takeoff Core's vocabulary rather than the engine's: mode

(single | range | multiple), minDate / maxDate, disabledDates,

allowedDates, disabledWeekDays, firstDayOfWeekIndex and size, each

mapped to the engine's Matcher / navigation props by a pure helper. value

is Date-based rather than Core's string: parsing and formatting a

dateFormat grammar in the wrapper would be behavior, not visual wrapping.

locale takes an object from react-day-picker/locale, since resolving a

locale string would mean bundling every locale.

Day contents can be customized with renderDay, which receives the date and

active modifiers while leaving the engine-owned day-button behavior intact.

The grid follows a value set from outside it. The engine reads defaultMonth

once and then owns the displayed month, so a preset button, a typed date or a

value restored from a form would select a day that is off-screen — every

caller had to hold a second piece of state and keep setValue and setMonth

in step. Setting the value is now enough. It is deliberately narrow: a passed

month wins outright (that parent is driving the grid), and only a selection

landing in a different month than the one on screen moves it, so navigation

the user performs is never undone.

```tsx

const [date, setDate] = useState<Date>();

<Calendar value={date} onValueChange={setDate} minDate={new Date()} />;

`

@takeoff-design/tokens ships the tk-calendar-* recipe. It is built on the

Figma datepicker.* token family — the one that describes a picking grid

(-items-* day cells, -header-* month row, -body-* grid, -footer-*) —

so day size, cell radius and every inset come from the design system rather

than from local values. The separate calendar.* family is left alone:

calendar-activity-* / calendar-week-cell-* belong to the activity

calendar, which is a different component.

That family is also why size has two values rather than three:

datepicker-items-base-size and -small-size are the only grid scales the

design defines — an input row has a large, a month grid does not.

Controlled-ness is decided by whether value is **passed**, not by whether it

currently holds a date — a picker's controlled value is undefined until

something is picked, so reading the value would make the ordinary

const [date, setDate] = useState<Date>() call site silently uncontrolled and

drop every parent-driven change after mount (a preset button, a reset, a saved

value arriving late).

Cells keep their own size rather than stretching: the grid is exactly as wide

as its seven columns, each one datepicker-items-*-size wide, with the

exported row gap between rows.

Day state is read from the attributes the engine already emits, with one

subtlety the design forced: in range mode the engine marks _every_ day of the

span data-selected, so the filled pill is scoped to the two ends and the

middle keeps the light band. The band itself is painted on a pseudo-element

rather than the cell, because border-radius is ignored on a table cell under

border-collapse: collapse — the layout the flush-column design needs.

Two Core header features have no react-day-picker counterpart, so the

wrapper supplies them. headerType (basic | divided | light | primary

| dark) is Core's tk-datepicker header vocabulary: basic divides the

month row from the grid, the rest drop that divider for a boxed surface, and

the two filled ones flip the label and arrows to white. The header also

carries Core's second pair of arrows: each pair steps one rung of the board it

is on — the single ones move a month, or a year on the year board; the double

ones move a year, or a whole twelve-year page.

view (day | month | year) is Core's view switch: the month and the

year in the caption are buttons that swap the day grid for a twelve-month or

twelve-year board. Which board shows is a third controlled pair — view +

onViewChange, with defaultView for the uncontrolled case, alongside the

ones for the selection and the displayed month. The boards are the one part of

the anatomy the engine does not render, so they are wrapper-owned —

role="grid" with one tab stop, arrow-key roving focus, aria-selected on

the current cell, focus moving into a board as it opens and back to the

trigger once a month is picked. Their accessible names come from the engine's

own labels.labelMonthDropdown / labelYearDropdown, so translating the

calendar translates the boards. Only the body is replaced: the displayed month

stays the engine's, through goToMonth. A dropdown* caption keeps the

engine's <select> pair instead of switch buttons and drops the year arrows,

since the year <select> covers them; the boards still work there.

One consequence worth calling out: since a board can replace the day grid on

any calendar, the body box is pinned on both — the day grid carries the same

min-height the month/year board does, so a four- or five-week month keeps

the six-week box instead of making the card shorter. Switching views does not

resize the card, paging months does not either, and fixedWeeks is not needed

to stop the month-to-month jump.

The card's width comes from the grid rather than the caption. The caption is

the month label plus a reserve for the arrows laid over it —

nav-button * 2 + h-padding at each end — so a label wider than what is left

over stretched the card, which then shrank again on the next arrow. The whole

card twitched as you paged, and in a popover the panel's edges moved with it.

A single-month calendar is therefore pinned, the caption takes that width

instead of setting it (min-width: 0, so its nowrap label is not its

automatic minimum), and a label long enough to reach the arrows ellipsizes. A

numberOfMonths > 1 row, legitimately wider than one grid, is left alone.

The card is pinned a little wider than the grid, through its own

--tk-calendar-frame-width (--tk-calendar-body-width + 24px, 336px at

base). The grid cannot absorb that difference itself — its width is divided

between seven table-layout: fixed columns, so widening it would move the day

cells off their exported size — so the grid keeps --tk-calendar-body-width

and centres inside the frame. Both scales take the same 24px; more would leave

the grid rattling in the card.

The navigation is sized on its own rather than on the day cell, through

--tk-calendar-nav-button-size (28px) and --tk-calendar-nav-icon-size

(20px). The two only ever shared a value by coincidence: 40px is a comfortable

day target and a needlessly wide arrow, and the header paid for it twice,

since the caption reserves nav-button * 2 + h-padding at each end. Four 40px

arrows took 192px of a 336px card and left the label 144px; at 28px the

reserve is 112px and the label gets 224px, which is what stopped a four-digit

year disappearing behind the ellipsis on a long month. The day grid is

untouched — cell-size still sizes the cells, the weekday row and the

week-number column.

Inside that caption the label takes the whole space between the arrows

(flex: 1) rather than sizing to its text. Otherwise the header stayed

restless even with the card pinned: "September 2026" and "May 2026" are

different widths, so the month and year buttons — and their hover boxes —

shifted on every arrow click. The box now stays put and only the text inside

it changes.

The same width reaches the two boxes between the card and the caption. The nav

is positioned against .tk-calendar-months, not against the card, and neither

that element nor the month inside it had a width of its own — both sized to

their content, so the month name still decided how wide they were. The card

centres them, so a narrower month moved both edges inward and the arrows,

inset from those edges, drifted with them. All four boxes now share

--tk-calendar-body-width, and a numberOfMonths > 1 row is exempt

throughout.

  • [#206](https://github.com/turkishtechnology/takeoff-v2/pull/206)

[a0da4d6](https://github.com/turkishtechnology/takeoff-v2/commit/a0da4d65d8dec7dc3fde0885d5dc706833a2e60c)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Two opt-in

classes for composing a date picker out of Popover and Calendar.

There is no DatePicker component — the pattern is documented instead. What the

design system supplies is the pair of corrections the composition needs, so

every picker looks the same without each app writing its own:

  • tk-datepicker-panel on Popover.Content lifts the bubble's 296px width

cap and its padding, so a calendar fits instead of being clipped, and drops

the grid's standalone border once it is floating.

  • tk-input-action on Popover.Trigger matches the in-field action buttons

when the trigger sits inside an Input. It belongs to Input rather than

to this pattern — it is the opt-in hook for any control a consumer places in

a field, and carries no rules of its own: it joins the lists the Input

recipe already uses, so an in-field trigger is Input.ClearButton's twin at

every size.

Neither is emitted automatically; apply them through classNames.

@takeoff-design/tailwind0.1.4
Patch Changes
  • Updated dependencies

[[f8b94f3](https://github.com/turkishtechnology/takeoff-v2/commit/f8b94f385b03fbc9d390fbbe6c39da57e0c3a4a9),

[a0da4d6](https://github.com/turkishtechnology/takeoff-v2/commit/a0da4d65d8dec7dc3fde0885d5dc706833a2e60c)]:

  • @takeoff-design/tokens@0.5.0

react-spar 0.2.0: ten new components, Table flagship, and API polish

The catalog roughly doubles. `@takeoff-ui/react-spar` 0.2.0 adds ten components — including the TanStack-backed `Table`, the compound `Alert`, and `Toast`/`Toaster` — alongside `Card`, `Chip`, `Dialog`, `Tabs`, `Breadcrumb`, `Spinner`, and `Label`. Existing components get prop and styling polish, and a few public APIs were renamed/removed; each change below ships with a one-line migration. The library deliberately stays on 0.x (no major bump) while it has a single consumer, so these are released under a minor.

New components

  • **Table** — the catalog’s first TanStack-backed component (@tanstack/react-table). A props-first <Table data columns getRowId /> with sorting, column filters in a Spar Popover, row selection, pagination, sticky header/columns, density, and a data-only getExportRows() projection.
  • **Alert** — compound Alert with variant/appearance, actions, and close handling.
  • **Toast** — Toast, Toaster, and createToaster powered by the Spar headless toast controller: types, positions, promise toasts, updates, persistent toasts, and overlap stacks.
  • Also new: **Card**, **Chip**, **Dialog**, **Tabs**, **Breadcrumb**, **Spinner**, and **Label**.
Breaking changes
  • **Button** — isLoading/isPressed renamed to loading/pressed.
    Before
    <Button isLoading isPressed />
    After
    <Button loading pressed />
  • **Radio / Checkbox** — the type prop (and the card variant) were removed. Wrap in your own bordered container if you need a card-style row.
    Before
    <Radio type="card" />
    After
    <Radio />
  • **Input** — Input.Container is removed; the bordered row is now the Input root, with Input.LeadingIcon / Input.TrailingIcon for icons.
    Before
    <Input>
      <Input.Container startContent={<Icon />}>
        <Input.Field />
      </Input.Container>
    </Input>
    After
    <Input>
      <Input.LeadingIcon>
        <Icon />
      </Input.LeadingIcon>
      <Input.Field />
    </Input>
  • **Drawer** — dismissable → dismissible, and Drawer.CloseButton → Drawer.Close (CSS class .tk-drawer-close-button → .tk-drawer-close).
    Before
    <Drawer dismissable={false}>
      <Drawer.CloseButton />
    </Drawer>
    After
    <Drawer dismissible={false}>
      <Drawer.Close />
    </Drawer>
  • **Tooltip / Popover** — the default Content variant changed from dark to white. Pass variant="dark" to keep the previous look.
  • **Select** — Select.Trigger now renders a disclosure chevron by default; pass indicator={false} to opt out.
Package details3 packages
@takeoff-ui/react-spar0.2.0
Minor Changes
  • [#110](https://github.com/turkishtechnology/takeoff-v2/pull/110)

[54e8401](https://github.com/turkishtechnology/takeoff-v2/commit/54e8401842b3e2ef5c39fb76b2b8e4e0695f922e)

Thanks [@harun-demir](https://github.com/harun-demir)! - Add accessible

labeling for the Chip remove button. Its accessible name defaults to

"Remove" and can be customized through the standard slot mechanism, matching

Alert.Close.

To set a custom label, pass it via the remove slot:

<Chip slotProps={{ remove: { 'aria-label': '…' } }} />.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Complete Input parity

with takeoff-ui: chips, counter, and styling fixes.

  • Added Input.Chips and Input.Chip for chips/tags inputs. Input.Chips

owns a string[] value (controlled value / onValueChange or

uncontrolled defaultValue), commits the trimmed field text on Enter or an

optional separator, removes the last tag on Backspace, and supports max

/ allowDuplicates. Input.Chip is a removable token with a focusable,

labelled remove button. v1 is string[]-only (no object values /

chipLabelKey / chipOptions / chipDisabled).

  • Added the **counter** treatment: placing Input.Decrement /

Input.Increment as direct children flanking Input.Field (outside

Input.Stepper) now centers the value and paints the step buttons in the

brand colour, matching takeoff-ui. No mode prop is introduced.

  • Added useControllableState and a PlaceholderAdd glyph.
  • Parity fixes: read-only field text is no longer dimmed (matches takeoff-ui),

and the strength-meter gap and prefix/suffix dividers now use design tokens

(--spacing-xs / --spacing-px).

  • Documented that masking/formatting is consumer-owned (via Input.Field

onChange), the password reveal is a keyboard-accessible toggle, and number

stepping is native-only.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Refactor Input compound

anatomy and complete design-system parity.

  • Removed Input.Container.
  • Moved the bordered row onto the Input root.
  • Added Input.LeadingIcon and Input.TrailingIcon.
  • Added Input.ClearButton, Input.Spinner, and Input.RevealButton.
  • Added Input.Strength, a four-segment password strength meter that grades

the field value and renders below the bordered row.

  • Added Input.Stepper, Input.Decrement, and Input.Increment for native

number input stepping.

  • Reshaped the placeholder eye / eye-off icons to the design system's Material

Symbols glyphs and added a matching lock icon.

  • Field.Description and Field.ErrorMessage now render a leading info /

error icon, matching the design system's helper-text anatomy.

**Breaking — migration.** Input.Container and its types/CSS are removed; the

bordered row is now the Input root itself.

  • Markup: move the field, prefix/suffix, and icons directly inside <Input>.

Old startContent / endContent move to the new Input.LeadingIcon /

Input.TrailingIcon parts (these render aria-hidden; for interactive

trailing content use Input.ClearButton / Input.RevealButton instead).

```tsx

// before (0.1.2)

<Input>

<Input.Container startContent={<SearchIcon />} endContent={<ClearIcon />}>

<Input.Field />

</Input.Container>

</Input>

// after

<Input>

<Input.LeadingIcon>

<SearchIcon />

</Input.LeadingIcon>

<Input.Field />

<Input.TrailingIcon>

<ClearIcon />

</Input.TrailingIcon>

</Input>

`

  • Types: replace InputContainerProps (and InputContainerOwnProps /

InputContainerSlot) with InputProps, or the relevant part props

(InputLeadingIconProps / InputTrailingIconProps).

  • CSS: retarget custom selectors — .tk-input-container → .tk-input (the

row box, border, :hover, :focus-within, and the [data-invalid] /

[data-disabled] / [data-readonly] state hooks now live on the

.tk-input root); .tk-input-start-content → .tk-input-leading-icon

(Input.LeadingIcon); .tk-input-end-content → .tk-input-trailing-icon

(Input.TrailingIcon).

  • [#120](https://github.com/turkishtechnology/takeoff-v2/pull/120)

[2d05b26](https://github.com/turkishtechnology/takeoff-v2/commit/2d05b268ab669aca55acf2e12e2377167b6e6028)

Thanks [@harun-demir](https://github.com/harun-demir)! - Add a customizable

disclosure indicator to Select, mirroring Accordion.Indicator.

Select.Trigger now renders a chevron at its trailing edge by default — it

flips direction and turns the primary color when open. The new indicator

prop on Select.Trigger overrides it: pass a node for a custom static icon, a

render function ({ isOpen }) => … to swap icons by open state, or false to

hide it. A standalone Select.Indicator compound part (default chevron +

render-prop children) is also available for full layout control inside the

trigger's render-prop children.

A render-function children on Select.Trigger opts out of the built-in

indicator and value wrapper entirely, so full-layout-control usages own every

node without a doubled chevron. The trigger's value region and indicator are

now addressable slots (value / indicator) via classNames / slotProps.

Chevrons come from the official @takeoff-icons/react set (outlined/rounded);

Accordion.Indicator is switched to the same icons so both stay consistent.

> **Heads-up (visual breaking) — released as minor on purpose.** Because the

> trigger now shows a chevron by default, existing Select usages gain a

> disclosure indicator without any code change. Per the 0.x release policy we

> ship this as minor (not major) while the library has a single consumer,

> to avoid churning the major version during this phase. Pass

> indicator={false} to opt out.

  • [#104](https://github.com/turkishtechnology/takeoff-v2/pull/104)

[8c11f76](https://github.com/turkishtechnology/takeoff-v2/commit/8c11f76c6bd2c38d3b9c911ec03625bd38260765)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Drawer:

rename the dismissable prop and the Drawer.CloseButton part, and add an

opt-in blur prop to Drawer.Overlay.

> **Heads-up for consumers — these are breaking renames.** They are released

> as minor (not major) on purpose while the library has a single consumer,

> to avoid churning the major version during this phase. A find-and-replace

> covers the migration:

>

> - **Prop:** <Drawer dismissable={false}> → <Drawer dismissible={false}>

> - **Part:** <Drawer.CloseButton> → <Drawer.Close>

> - **Type:** DrawerCloseButtonProps → DrawerCloseProps

> - **CSS class:** .tk-drawer-close-button → .tk-drawer-close

>

> New: <Drawer.Overlay blur /> adds a soft backdrop blur (emits

> data-blur).

  • [#120](https://github.com/turkishtechnology/takeoff-v2/pull/120)

[2d05b26](https://github.com/turkishtechnology/takeoff-v2/commit/2d05b268ab669aca55acf2e12e2377167b6e6028)

Thanks [@harun-demir](https://github.com/harun-demir)! - Add the props-first

Table component (RFC §docs/rfc-table-component.md) — the catalog's first

TanStack-backed component (state engine is @tanstack/react-table, not a Spar

primitive). Phase 1 ships a single <Table data columns getRowId /> surface

plus the column-def + slot escape hatch (Tier 1.5): custom cell/header

render-props, meta/align/sticky/ width cell-container knobs,

multi-sort with aria-sort, column filters in a Spar Popover

(text/checkbox/radio), expandable rows, client/server data (manual + bundled

onDataRequest), row selection (single/multiple + select-all) via Spar

Checkbox, pagination via Spar Select/Button, sticky header + pinned

columns with the documented border-collapse: separate z-index/offset

contract, density (size)/striped, native <table> a11y, and the data-only

getExportRows() projection (the export engine stays consumer-side). The full

compound surface (Tier 2) and grouping/virtualization/ inline-edit are

deferred to post-Phase 1 and do not break the Phase 1 API.

Patch Changes
  • [#109](https://github.com/turkishtechnology/takeoff-v2/pull/109)

[7e04360](https://github.com/turkishtechnology/takeoff-v2/commit/7e0436013b47f3169a0a7bc1efa421a60d81744e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the

compound Alert component with variant and appearance styling, actions, close

handling, docs, and token-driven alert recipes.

  • [#112](https://github.com/turkishtechnology/takeoff-v2/pull/112)

[9f778a8](https://github.com/turkishtechnology/takeoff-v2/commit/9f778a8c056c20a78d1afa402fb970163072491f)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the new

Spinner component with size, variant, and appearance options, accessibility

defaults, decorative aria-hidden handling, token recipe styles, and

documentation.

  • [#103](https://github.com/turkishtechnology/takeoff-v2/pull/103)

[624a7f4](https://github.com/turkishtechnology/takeoff-v2/commit/624a7f4a37c8f4645bd95cf50175323c37c55a08)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add Card

component with compound sections, styles, and docs

  • [#94](https://github.com/turkishtechnology/takeoff-v2/pull/94)

[36b535d](https://github.com/turkishtechnology/takeoff-v2/commit/36b535d7bcfc9be7e1de8bd170d41a58e2920a78)

Thanks [@ulasturann](https://github.com/ulasturann)! - Add the compound

Breadcrumb component (Breadcrumb, BreadcrumbList, BreadcrumbItem,

BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator) along with its

design-system styles and docs.

  • [#132](https://github.com/turkishtechnology/takeoff-v2/pull/132)

[332b98c](https://github.com/turkishtechnology/takeoff-v2/commit/332b98c057f5c5de4509656bb382a9b6e2470e1e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Fix React

SPAR documentation and wrapper edge cases:

  • Update the README to match the current exported component surface (including

Table and Toast) and provider behavior.

  • Compose slotProps event handlers for Alert.Close, Chip, and the

Input.Increment / Input.Decrement / Input.ClearButton /

Input.RevealButton controls instead of letting them replace internal

behavior.

  • Keep Alert.Close's onClose unconditional: a decorative onClick that

calls preventDefault no longer suppresses the alert's dismissal.

  • Stop a non-disabled Chip keydown handler and a removable-only Chip's

remove click from leaking past the disabled guard / from blocking event

bubbling.

  • Preserve custom non-dismissible dismiss handlers in Drawer.Panel, matching

Dialog.Panel.

  • Preserve renderable falsy content such as 0 in Button and Badge slots

while keeping empty-string content from rendering an empty wrapper, via a

shared isRenderableNode helper used consistently across Button, Badge,

Chip, and Alert.Close.

  • [#107](https://github.com/turkishtechnology/takeoff-v2/pull/107)

[6b26c62](https://github.com/turkishtechnology/takeoff-v2/commit/6b26c62e303fff9d809f9aae920623adedef4c81)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the new

Chip component to @takeoff-ui/react-spar with token-driven styles in

@takeoff-design/tokens.

The new component includes clickable and removable variants,

keyboard-accessible interaction behavior, disabled states, and matching visual

feedback for interactive chips.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Fix Input action and

field correctness bugs:

  • Input.Increment / Input.Decrement no longer throw when composed with a

non-number field — stepUp() / stepDown() are now guarded so a text input

is a safe no-op instead of an uncaught InvalidStateError.

  • Field.Description and Field.ErrorMessage no longer render a stray

leading icon when they have no (or empty) content.

  • The password reveal state is reset when the field unmounts, so a later

password field can't mount already revealed and leak its value as plain

text.

  • Input.RevealButton re-hides the password on form submit even when the form

or field mounts after the button.

  • useControllableState latches controlled vs. uncontrolled mode on the first

render (matching React's native inputs), so a value that flips between

undefined and a real value no longer silently drops internal state.

  • Input.Strength is hoisted below the bordered row by component reference

instead of a displayName string, so it still works in minified builds.

  • Perf: the Input context value is memoized (consumers no longer re-render on

every keystroke) and Input.Field's value-mirror effect only runs when the

controlled value changes rather than on every render.

  • Internal: centralize the native-value-setter and number-stepping DOM helpers

shared by the action parts.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - More Input fixes from

the review:

  • Input.Chips ignores keystrokes during IME composition, so pressing Enter

to confirm a CJK candidate no longer commits a half-composed chip.

  • Input.Chips's key handling now re-binds when the field element is replaced

(e.g. a re-keyed/remounted Input.Field), so Enter/Backspace keep working.

  • Input.ClearButton now clears chips too: it stays visible while there are

committed tags (not only typed text) and one click wipes the typed text and

every tag. Content-owning parts register a reset with the Input so a single

clear empties the whole field.

  • Drop 5 dead positional parameters from the input-size SCSS mixin (their

bodies were removed in the anatomy refactor); compiled CSS is unchanged.

  • [f6036d6](https://github.com/turkishtechnology/takeoff-v2/commit/f6036d672cf6c5c0b63c5d4097f15f5b65038da4)

Thanks [@harun-demir](https://github.com/harun-demir)! - Widen the

@takeoff-design/tokens peer dependency to >=0.1.2 <1.0.0 and enable

Changesets' onlyUpdatePeerDependentsWhenOutOfRange so a tokens minor (0.2.0)

no longer force-bumps @takeoff-ui/react-spar to a major. react-spar stays

0.x.

  • [#101](https://github.com/turkishtechnology/takeoff-v2/pull/101)

[f3c5c27](https://github.com/turkishtechnology/takeoff-v2/commit/f3c5c2744fb2a495abe3edb00412468e611c15c7)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add dialog

component

  • [#99](https://github.com/turkishtechnology/takeoff-v2/pull/99)

[7efc892](https://github.com/turkishtechnology/takeoff-v2/commit/7efc8922d79cef1f3cb00c9ba6899d39492f7474)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add

optional prop to field component

  • [#88](https://github.com/turkishtechnology/takeoff-v2/pull/88)

[aeb9c58](https://github.com/turkishtechnology/takeoff-v2/commit/aeb9c587fe126e143b125420df7066fcb037ae4e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

**Breaking:** Removed the type prop ('default' | 'card') from Radio,

together with the card variant. The exported RadioType type, the

data-type attribute on .tk-radio / .tk-radio-item, and the

[data-type='card'] style recipe are all gone.

Updated Radio styles and docs after the API removal, including focus,

disabled, and invalid focus visuals for the remaining variants.

Migration:

  • Remove the type prop: <Radio type="card"> → <Radio> (and drop the

no-op type="default"). The built-in card (bordered-row) presentation no

longer exists — wrap the radio in your own bordered container (or the

Takeoff Card tokens) if you need it.

  • Remove any import type { RadioType } from '@takeoff-ui/react-spar'; there

is no replacement type.

  • Retarget custom CSS: selectors on .tk-radio[data-type='card'] /

.tk-radio-item[data-type='card'] will never match again — replace them

with your own class/wrapper.

  • [#51](https://github.com/turkishtechnology/takeoff-v2/pull/51)

[6260d67](https://github.com/turkishtechnology/takeoff-v2/commit/6260d6711b2e08ea00668b0faaa194167fed732d)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add teal,

white, dark badge variants; fix outlined border colors and update dot size to

8px

  • [#89](https://github.com/turkishtechnology/takeoff-v2/pull/89)

[548cfc4](https://github.com/turkishtechnology/takeoff-v2/commit/548cfc42e92ee3716582d2f0a13be1967301de8e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

**Breaking:** Removed the type prop ('default' | 'card') from Checkbox,

together with the card variant. The exported CheckboxType type, the

data-type attribute on the checkbox root, and the

.tk-checkbox[data-type='card'] style recipe are all gone.

Updated Checkbox styles and docs to match the simplified API, including

disabled, focus, and invalid focus visuals for the remaining default variant.

Migration:

  • Remove the type prop: <Checkbox type="card"> → <Checkbox> (and drop

the no-op type="default"). The built-in card (bordered-row) presentation

no longer exists — wrap the checkbox in your own bordered container (or the

Takeoff Card tokens) if you need it.

  • Remove any import type { CheckboxType } from '@takeoff-ui/react-spar';

there is no replacement type.

  • Retarget custom CSS: selectors on .tk-checkbox[data-type='card'] /

[data-type='default'] will never match again — replace them with your own

class/wrapper.

  • [#100](https://github.com/turkishtechnology/takeoff-v2/pull/100)

[9263be0](https://github.com/turkishtechnology/takeoff-v2/commit/9263be048ad5cabbe9a24238514f6f869395a501)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add Tabs

component built on the Spar primitive, with horizontal and vertical

orientation, four appearances (basic, compact, divided, expanded),

three color variants (primary, info, neutral), and three sizes (small,

base, large). Exposes the compound Tabs.List / Tabs.Trigger /

Tabs.Content API and forwards Spar's controlled/uncontrolled selection

state.

  • [#124](https://github.com/turkishtechnology/takeoff-v2/pull/124)

[e5d2339](https://github.com/turkishtechnology/takeoff-v2/commit/e5d2339ae392c2e44af2373cfb3c90b3484b2f4a)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - **Breaking

(visual default):** Changed the default variant of Tooltip.Content and

Popover.Content from dark to white. Default (no-prop) usages now render

differently and emit data-variant="white" instead of data-variant="dark"

on the content slot.

Migration:

  • To keep the previous appearance, set the variant explicitly:

<Tooltip.Content variant="dark"> / <Popover.Content variant="dark">.

  • Retarget any CSS/selectors on [data-variant="dark"] for the default state,

since the default now stamps data-variant="white".

  • [#119](https://github.com/turkishtechnology/takeoff-v2/pull/119)

[04ecf50](https://github.com/turkishtechnology/takeoff-v2/commit/04ecf504f641fa81305cb5f354ac92a1c6ea069a)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the

Toast component and toaster styles.

  • Added React Spar Toast, Toaster, and createToaster exports powered by

the Spar headless toast controller.

  • Added default Alert-based toast rendering with title, description, close,

and action anatomy.

  • Added support for toast types, appearances, promise toasts, updates,

persistent toasts, duration, max visible toasts, page-idle pausing, custom

rendering, positions, and overlap stacks.

  • Added token recipe styles for toast viewport placement, default layout,

enter/exit motion, overlap expansion, width constraints, and stacked z-index

ordering.

  • Added Toast documentation, API tables, and demos.
  • [#97](https://github.com/turkishtechnology/takeoff-v2/pull/97)

[39000d2](https://github.com/turkishtechnology/takeoff-v2/commit/39000d2fda272e631550604f2389187a78ea1091)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Create

label component

  • [#66](https://github.com/turkishtechnology/takeoff-v2/pull/66)

[ac14c1d](https://github.com/turkishtechnology/takeoff-v2/commit/ac14c1d8d3b624a3d1b6402bce77102b7df0a92d)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - **Breaking

(Button):** Renamed the isLoading and isPressed props to loading and

pressed.

Migration — find-and-replace on <Button> usages:

  • <Button isLoading> / isLoading={x} → <Button loading> / loading={x}
  • <Button isPressed> / isPressed={x} → <Button pressed> / pressed={x}

onPressedChange is unchanged.

@takeoff-design/tokens0.2.0
Minor Changes
  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Complete Input parity

with takeoff-ui: chips, counter, and styling fixes.

  • Added Input.Chips and Input.Chip for chips/tags inputs. Input.Chips

owns a string[] value (controlled value / onValueChange or

uncontrolled defaultValue), commits the trimmed field text on Enter or an

optional separator, removes the last tag on Backspace, and supports max

/ allowDuplicates. Input.Chip is a removable token with a focusable,

labelled remove button. v1 is string[]-only (no object values /

chipLabelKey / chipOptions / chipDisabled).

  • Added the **counter** treatment: placing Input.Decrement /

Input.Increment as direct children flanking Input.Field (outside

Input.Stepper) now centers the value and paints the step buttons in the

brand colour, matching takeoff-ui. No mode prop is introduced.

  • Added useControllableState and a PlaceholderAdd glyph.
  • Parity fixes: read-only field text is no longer dimmed (matches takeoff-ui),

and the strength-meter gap and prefix/suffix dividers now use design tokens

(--spacing-xs / --spacing-px).

  • Documented that masking/formatting is consumer-owned (via Input.Field

onChange), the password reveal is a keyboard-accessible toggle, and number

stepping is native-only.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Refactor Input compound

anatomy and complete design-system parity.

  • Removed Input.Container.
  • Moved the bordered row onto the Input root.
  • Added Input.LeadingIcon and Input.TrailingIcon.
  • Added Input.ClearButton, Input.Spinner, and Input.RevealButton.
  • Added Input.Strength, a four-segment password strength meter that grades

the field value and renders below the bordered row.

  • Added Input.Stepper, Input.Decrement, and Input.Increment for native

number input stepping.

  • Reshaped the placeholder eye / eye-off icons to the design system's Material

Symbols glyphs and added a matching lock icon.

  • Field.Description and Field.ErrorMessage now render a leading info /

error icon, matching the design system's helper-text anatomy.

**Breaking — migration.** Input.Container and its types/CSS are removed; the

bordered row is now the Input root itself.

  • Markup: move the field, prefix/suffix, and icons directly inside <Input>.

Old startContent / endContent move to the new Input.LeadingIcon /

Input.TrailingIcon parts (these render aria-hidden; for interactive

trailing content use Input.ClearButton / Input.RevealButton instead).

```tsx

// before (0.1.2)

<Input>

<Input.Container startContent={<SearchIcon />} endContent={<ClearIcon />}>

<Input.Field />

</Input.Container>

</Input>

// after

<Input>

<Input.LeadingIcon>

<SearchIcon />

</Input.LeadingIcon>

<Input.Field />

<Input.TrailingIcon>

<ClearIcon />

</Input.TrailingIcon>

</Input>

`

  • Types: replace InputContainerProps (and InputContainerOwnProps /

InputContainerSlot) with InputProps, or the relevant part props

(InputLeadingIconProps / InputTrailingIconProps).

  • CSS: retarget custom selectors — .tk-input-container → .tk-input (the

row box, border, :hover, :focus-within, and the [data-invalid] /

[data-disabled] / [data-readonly] state hooks now live on the

.tk-input root); .tk-input-start-content → .tk-input-leading-icon

(Input.LeadingIcon); .tk-input-end-content → .tk-input-trailing-icon

(Input.TrailingIcon).

  • [#104](https://github.com/turkishtechnology/takeoff-v2/pull/104)

[8c11f76](https://github.com/turkishtechnology/takeoff-v2/commit/8c11f76c6bd2c38d3b9c911ec03625bd38260765)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Drawer:

rename the dismissable prop and the Drawer.CloseButton part, and add an

opt-in blur prop to Drawer.Overlay.

> **Heads-up for consumers — these are breaking renames.** They are released

> as minor (not major) on purpose while the library has a single consumer,

> to avoid churning the major version during this phase. A find-and-replace

> covers the migration:

>

> - **Prop:** <Drawer dismissable={false}> → <Drawer dismissible={false}>

> - **Part:** <Drawer.CloseButton> → <Drawer.Close>

> - **Type:** DrawerCloseButtonProps → DrawerCloseProps

> - **CSS class:** .tk-drawer-close-button → .tk-drawer-close

>

> New: <Drawer.Overlay blur /> adds a soft backdrop blur (emits

> data-blur).

Patch Changes
  • [#109](https://github.com/turkishtechnology/takeoff-v2/pull/109)

[7e04360](https://github.com/turkishtechnology/takeoff-v2/commit/7e0436013b47f3169a0a7bc1efa421a60d81744e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the

compound Alert component with variant and appearance styling, actions, close

handling, docs, and token-driven alert recipes.

  • [#126](https://github.com/turkishtechnology/takeoff-v2/pull/126)

[98c787d](https://github.com/turkishtechnology/takeoff-v2/commit/98c787d2cbb0fd81766a882cfe792f91d49a9395)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Update

colors of headers for dark mode in card, dialog and drawer components

  • [#112](https://github.com/turkishtechnology/takeoff-v2/pull/112)

[9f778a8](https://github.com/turkishtechnology/takeoff-v2/commit/9f778a8c056c20a78d1afa402fb970163072491f)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the new

Spinner component with size, variant, and appearance options, accessibility

defaults, decorative aria-hidden handling, token recipe styles, and

documentation.

  • [#103](https://github.com/turkishtechnology/takeoff-v2/pull/103)

[624a7f4](https://github.com/turkishtechnology/takeoff-v2/commit/624a7f4a37c8f4645bd95cf50175323c37c55a08)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add Card

component with compound sections, styles, and docs

  • [#94](https://github.com/turkishtechnology/takeoff-v2/pull/94)

[36b535d](https://github.com/turkishtechnology/takeoff-v2/commit/36b535d7bcfc9be7e1de8bd170d41a58e2920a78)

Thanks [@ulasturann](https://github.com/ulasturann)! - Add the compound

Breadcrumb component (Breadcrumb, BreadcrumbList, BreadcrumbItem,

BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator) along with its

design-system styles and docs.

  • [#107](https://github.com/turkishtechnology/takeoff-v2/pull/107)

[6b26c62](https://github.com/turkishtechnology/takeoff-v2/commit/6b26c62e303fff9d809f9aae920623adedef4c81)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the new

Chip component to @takeoff-ui/react-spar with token-driven styles in

@takeoff-design/tokens.

The new component includes clickable and removable variants,

keyboard-accessible interaction behavior, disabled states, and matching visual

feedback for interactive chips.

  • [#131](https://github.com/turkishtechnology/takeoff-v2/pull/131)

[b05ba5f](https://github.com/turkishtechnology/takeoff-v2/commit/b05ba5f5786aced57fae99ab50c64e12ef310445)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Update

close button styles in alert dialog and drawer components

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - More Input fixes from

the review:

  • Input.Chips ignores keystrokes during IME composition, so pressing Enter

to confirm a CJK candidate no longer commits a half-composed chip.

  • Input.Chips's key handling now re-binds when the field element is replaced

(e.g. a re-keyed/remounted Input.Field), so Enter/Backspace keep working.

  • Input.ClearButton now clears chips too: it stays visible while there are

committed tags (not only typed text) and one click wipes the typed text and

every tag. Content-owning parts register a reset with the Input so a single

clear empties the whole field.

  • Drop 5 dead positional parameters from the input-size SCSS mixin (their

bodies were removed in the anatomy refactor); compiled CSS is unchanged.

  • [#56](https://github.com/turkishtechnology/takeoff-v2/pull/56)

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9)

Thanks [@ulasturann](https://github.com/ulasturann)! - Input: gate the counter

look on an explicit data-layout="counter" hook.

The counter treatment (centered value with brand-coloured flanking

increment/decrement buttons) is selected by setting data-layout="counter" on

the Input root. Without the attribute, the same markup renders as a plain

left-aligned number field, so the styled treatment is an explicit, documented

opt-in rather than something inferred from element placement.

```tsx

<Input data-layout="counter">

<Input.Decrement />

<Input.Field type="number" />

<Input.Increment />

</Input>

`

The change is entirely in the tk-input recipe; no @takeoff-ui/react-spar

component code changes (the Input root already forwards arbitrary data-*

attributes to the DOM).

  • [#91](https://github.com/turkishtechnology/takeoff-v2/pull/91)

[f9a14e8](https://github.com/turkishtechnology/takeoff-v2/commit/f9a14e8b54ed87790ae8ea06825aefd771e4148a)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Update the

styling of the switch component according to the design system

  • [#92](https://github.com/turkishtechnology/takeoff-v2/pull/92)

[bcfc8c4](https://github.com/turkishtechnology/takeoff-v2/commit/bcfc8c45cd4c40d17f4446c433f6e8c723ccc637)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Update

styles of the select component according to the design system

  • [#65](https://github.com/turkishtechnology/takeoff-v2/pull/65)

[aa60d5b](https://github.com/turkishtechnology/takeoff-v2/commit/aa60d5b2fa4be70acdc3a9eaa06c55aa7c1ef5ba)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Refine

button styles

  • [#64](https://github.com/turkishtechnology/takeoff-v2/pull/64)

[c5ab13f](https://github.com/turkishtechnology/takeoff-v2/commit/c5ab13fcdce3af5532b9e328da045204ac51a19b)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Refine

drawer styles

  • [#101](https://github.com/turkishtechnology/takeoff-v2/pull/101)

[f3c5c27](https://github.com/turkishtechnology/takeoff-v2/commit/f3c5c2744fb2a495abe3edb00412468e611c15c7)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add dialog

component

  • [#63](https://github.com/turkishtechnology/takeoff-v2/pull/63)

[c64f340](https://github.com/turkishtechnology/takeoff-v2/commit/c64f340050781f46c62b5310055bf3c0aa0dc961)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Refine

popover styles

  • [#120](https://github.com/turkishtechnology/takeoff-v2/pull/120)

[2d05b26](https://github.com/turkishtechnology/takeoff-v2/commit/2d05b268ab669aca55acf2e12e2377167b6e6028)

Thanks [@harun-demir](https://github.com/harun-demir)! - Add a customizable

disclosure indicator to Select, mirroring Accordion.Indicator.

Select.Trigger now renders a chevron at its trailing edge by default — it

flips direction and turns the primary color when open. The new indicator

prop on Select.Trigger overrides it: pass a node for a custom static icon, a

render function ({ isOpen }) => … to swap icons by open state, or false to

hide it. A standalone Select.Indicator compound part (default chevron +

render-prop children) is also available for full layout control inside the

trigger's render-prop children.

A render-function children on Select.Trigger opts out of the built-in

indicator and value wrapper entirely, so full-layout-control usages own every

node without a doubled chevron. The trigger's value region and indicator are

now addressable slots (value / indicator) via classNames / slotProps.

Chevrons come from the official @takeoff-icons/react set (outlined/rounded);

Accordion.Indicator is switched to the same icons so both stay consistent.

> **Heads-up (visual breaking) — released as minor on purpose.** Because the

> trigger now shows a chevron by default, existing Select usages gain a

> disclosure indicator without any code change. Per the 0.x release policy we

> ship this as minor (not major) while the library has a single consumer,

> to avoid churning the major version during this phase. Pass

> indicator={false} to opt out.

  • [#88](https://github.com/turkishtechnology/takeoff-v2/pull/88)

[aeb9c58](https://github.com/turkishtechnology/takeoff-v2/commit/aeb9c587fe126e143b125420df7066fcb037ae4e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

**Breaking:** Removed the type prop ('default' | 'card') from Radio,

together with the card variant. The exported RadioType type, the

data-type attribute on .tk-radio / .tk-radio-item, and the

[data-type='card'] style recipe are all gone.

Updated Radio styles and docs after the API removal, including focus,

disabled, and invalid focus visuals for the remaining variants.

Migration:

  • Remove the type prop: <Radio type="card"> → <Radio> (and drop the

no-op type="default"). The built-in card (bordered-row) presentation no

longer exists — wrap the radio in your own bordered container (or the

Takeoff Card tokens) if you need it.

  • Remove any import type { RadioType } from '@takeoff-ui/react-spar'; there

is no replacement type.

  • Retarget custom CSS: selectors on .tk-radio[data-type='card'] /

.tk-radio-item[data-type='card'] will never match again — replace them

with your own class/wrapper.

  • [#51](https://github.com/turkishtechnology/takeoff-v2/pull/51)

[6260d67](https://github.com/turkishtechnology/takeoff-v2/commit/6260d6711b2e08ea00668b0faaa194167fed732d)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add teal,

white, dark badge variants; fix outlined border colors and update dot size to

8px

  • [#89](https://github.com/turkishtechnology/takeoff-v2/pull/89)

[548cfc4](https://github.com/turkishtechnology/takeoff-v2/commit/548cfc42e92ee3716582d2f0a13be1967301de8e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! -

**Breaking:** Removed the type prop ('default' | 'card') from Checkbox,

together with the card variant. The exported CheckboxType type, the

data-type attribute on the checkbox root, and the

.tk-checkbox[data-type='card'] style recipe are all gone.

Updated Checkbox styles and docs to match the simplified API, including

disabled, focus, and invalid focus visuals for the remaining default variant.

Migration:

  • Remove the type prop: <Checkbox type="card"> → <Checkbox> (and drop

the no-op type="default"). The built-in card (bordered-row) presentation

no longer exists — wrap the checkbox in your own bordered container (or the

Takeoff Card tokens) if you need it.

  • Remove any import type { CheckboxType } from '@takeoff-ui/react-spar';

there is no replacement type.

  • Retarget custom CSS: selectors on .tk-checkbox[data-type='card'] /

[data-type='default'] will never match again — replace them with your own

class/wrapper.

  • [#100](https://github.com/turkishtechnology/takeoff-v2/pull/100)

[9263be0](https://github.com/turkishtechnology/takeoff-v2/commit/9263be048ad5cabbe9a24238514f6f869395a501)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add Tabs

component built on the Spar primitive, with horizontal and vertical

orientation, four appearances (basic, compact, divided, expanded),

three color variants (primary, info, neutral), and three sizes (small,

base, large). Exposes the compound Tabs.List / Tabs.Trigger /

Tabs.Content API and forwards Spar's controlled/uncontrolled selection

state.

  • [#119](https://github.com/turkishtechnology/takeoff-v2/pull/119)

[04ecf50](https://github.com/turkishtechnology/takeoff-v2/commit/04ecf504f641fa81305cb5f354ac92a1c6ea069a)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Add the

Toast component and toaster styles.

  • Added React Spar Toast, Toaster, and createToaster exports powered by

the Spar headless toast controller.

  • Added default Alert-based toast rendering with title, description, close,

and action anatomy.

  • Added support for toast types, appearances, promise toasts, updates,

persistent toasts, duration, max visible toasts, page-idle pausing, custom

rendering, positions, and overlap stacks.

  • Added token recipe styles for toast viewport placement, default layout,

enter/exit motion, overlap expansion, width constraints, and stacked z-index

ordering.

  • Added Toast documentation, API tables, and demos.
  • [#62](https://github.com/turkishtechnology/takeoff-v2/pull/62)

[d9eed3b](https://github.com/turkishtechnology/takeoff-v2/commit/d9eed3b19fa9ac2a11cc7d3809adc6c172261a1e)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Refine

tooltip styles

  • [#97](https://github.com/turkishtechnology/takeoff-v2/pull/97)

[39000d2](https://github.com/turkishtechnology/takeoff-v2/commit/39000d2fda272e631550604f2389187a78ea1091)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Create

label component

  • [#93](https://github.com/turkishtechnology/takeoff-v2/pull/93)

[1363a3b](https://github.com/turkishtechnology/takeoff-v2/commit/1363a3b5273cac88f3582b3f14269c7d75a52612)

Thanks [@pinaryalcinduran](https://github.com/pinaryalcinduran)! - Set tk-font

@takeoff-design/tailwind0.1.1
Patch Changes
  • Updated dependencies

[[7e04360](https://github.com/turkishtechnology/takeoff-v2/commit/7e0436013b47f3169a0a7bc1efa421a60d81744e),

[98c787d](https://github.com/turkishtechnology/takeoff-v2/commit/98c787d2cbb0fd81766a882cfe792f91d49a9395),

[9f778a8](https://github.com/turkishtechnology/takeoff-v2/commit/9f778a8c056c20a78d1afa402fb970163072491f),

[624a7f4](https://github.com/turkishtechnology/takeoff-v2/commit/624a7f4a37c8f4645bd95cf50175323c37c55a08),

[36b535d](https://github.com/turkishtechnology/takeoff-v2/commit/36b535d7bcfc9be7e1de8bd170d41a58e2920a78),

[6b26c62](https://github.com/turkishtechnology/takeoff-v2/commit/6b26c62e303fff9d809f9aae920623adedef4c81),

[b05ba5f](https://github.com/turkishtechnology/takeoff-v2/commit/b05ba5f5786aced57fae99ab50c64e12ef310445),

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9),

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9),

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9),

[520fcd0](https://github.com/turkishtechnology/takeoff-v2/commit/520fcd0983739abcfbf277ed9f0b85a3be6739b9),

[f9a14e8](https://github.com/turkishtechnology/takeoff-v2/commit/f9a14e8b54ed87790ae8ea06825aefd771e4148a),

[bcfc8c4](https://github.com/turkishtechnology/takeoff-v2/commit/bcfc8c45cd4c40d17f4446c433f6e8c723ccc637),

[aa60d5b](https://github.com/turkishtechnology/takeoff-v2/commit/aa60d5b2fa4be70acdc3a9eaa06c55aa7c1ef5ba),

[c5ab13f](https://github.com/turkishtechnology/takeoff-v2/commit/c5ab13fcdce3af5532b9e328da045204ac51a19b),

[f3c5c27](https://github.com/turkishtechnology/takeoff-v2/commit/f3c5c2744fb2a495abe3edb00412468e611c15c7),

[c64f340](https://github.com/turkishtechnology/takeoff-v2/commit/c64f340050781f46c62b5310055bf3c0aa0dc961),

[2d05b26](https://github.com/turkishtechnology/takeoff-v2/commit/2d05b268ab669aca55acf2e12e2377167b6e6028),

[aeb9c58](https://github.com/turkishtechnology/takeoff-v2/commit/aeb9c587fe126e143b125420df7066fcb037ae4e),

[6260d67](https://github.com/turkishtechnology/takeoff-v2/commit/6260d6711b2e08ea00668b0faaa194167fed732d),

[548cfc4](https://github.com/turkishtechnology/takeoff-v2/commit/548cfc42e92ee3716582d2f0a13be1967301de8e),

[8c11f76](https://github.com/turkishtechnology/takeoff-v2/commit/8c11f76c6bd2c38d3b9c911ec03625bd38260765),

[9263be0](https://github.com/turkishtechnology/takeoff-v2/commit/9263be048ad5cabbe9a24238514f6f869395a501),

[04ecf50](https://github.com/turkishtechnology/takeoff-v2/commit/04ecf504f641fa81305cb5f354ac92a1c6ea069a),

[d9eed3b](https://github.com/turkishtechnology/takeoff-v2/commit/d9eed3b19fa9ac2a11cc7d3809adc6c172261a1e),

[39000d2](https://github.com/turkishtechnology/takeoff-v2/commit/39000d2fda272e631550604f2389187a78ea1091),

[1363a3b](https://github.com/turkishtechnology/takeoff-v2/commit/1363a3b5273cac88f3582b3f14269c7d75a52612)]:

  • @takeoff-design/tokens@0.2.0

@takeoff-design/tailwind ships its first public version

`@takeoff-design/tailwind` is now on npm. It ships the Tailwind theme that mirrors the @takeoff-design/tokens design system — a Tailwind v4 stylesheet at the package root and a Tailwind v3 plugin module under `./v3` — so teams on either Tailwind major can adopt Takeoff styling without re-deriving tokens by hand. From this release on, tailwind follows the same Changesets and trusted-publishing flow as tokens and react-spar.

Highlights

  • **Tailwind v4 theme.** Import the package directly to load the generated @theme block driven by @takeoff-design/tokens. Works with @import in any v4 setup.
    After
    @import 'tailwindcss';
    @import '@takeoff-design/tailwind';
  • **Tailwind v3 plugin.** A drop-in plugin under ./v3 exposes the same token surface as a v3-compatible theme extension. Use it via tailwind.config.js.
    After
    // tailwind.config.js
    module.exports = {
      presets: [require('@takeoff-design/tailwind/v3')],
    };
  • Single source of truth — both entry points are generated from @takeoff-design/tokens, so consumers stay in sync with the design system without copying values into a local config.

How to install

  • Install alongside Tailwind. The package only declares a peer on tailwindcss >= 3, so it composes with whatever Tailwind version your app already uses.
    After
    pnpm add @takeoff-design/tailwind
    # or
    npm install @takeoff-design/tailwind
Package details1 package
@takeoff-design/tailwind0.1.0
Minor Changes
  • cd6dd52: Initial public release of @takeoff-design/tailwind. Ships the

Tailwind CSS v4 theme stylesheet (./v4, ./ default) and the v3 plugin

module (./v3) driven by @takeoff-design/tokens. From this version on,

tailwind is part of the Changesets release loop and follows the same

versioning and trusted publishing flow as @takeoff-design/tokens and

@takeoff-ui/react-spar.

Select polish: Spar 0.2.0-beta.1, Figma-aligned styles, contentWidth

A focused beta refining the Select compound. The wrapper migrates onto Spar 0.2.0-beta.1, which removes Select.Value and Select.ItemText in favor of native props on Trigger and Item. Panel and item styles are now driven by the Figma dropdown token family, and a new contentWidth prop replaces the shrink-wrapped panel with a trigger-aligned default that tracks responsive resizes.

Highlights

  • Select panel and item styles aligned with Figma — --dropdown-items-basic-* tokens drive item padding/gap/radius, hover background switches to --background-lightest, panel radius uses --radius-m-base, and the shadow becomes the layered Effect1-Default-Sm from the design source.
  • New contentWidth prop on Select controls panel width — defaults to 'trigger' (panel matches the trigger's measured width via ResizeObserver); also accepts 'content', a number, or any CSS string. Consumer style.width still wins.

Breaking changes

  • Select.Value removed. Placeholder now lives on Select.Trigger.
    Before
    <Select.Trigger>
      <Select.Value placeholder="Pick one" />
    </Select.Trigger>
    After
    <Select.Trigger placeholder="Pick one" />
  • Select.ItemText removed. Item text is provided via the new label prop and rendered as direct children.
    Before
    <Select.Item value="tr">
      <Select.ItemText>Türkiye</Select.ItemText>
    </Select.Item>
    After
    <Select.Item value="tr" label="Türkiye">
      Türkiye
    </Select.Item>
  • Select.Item.textValue renamed to label — aligns with native <option label>. The new label is also the typeahead search key.
    Before
    <Select.Item textValue="Türkiye">
    After
    <Select.Item label="Türkiye">
Package details2 packages
@takeoff-ui/react-spar0.1.2
Patch Changes
  • 415e689: Refresh release to recover the latest dist-tag.

The 0.1.1 release stalled in an inconsistent state: the auto-snapshot step

published 0.1.1 (instead of 0.1.1-next-<sha>) to the next dist-tag, and

when the Version Packages PR for 0.1.1 was merged the stable publish was

skipped with "version 0.1.1 is already published." That left latest pointing

at 0.1.0 while next pointed at 0.1.1.

This bump publishes both packages as 0.1.2 and restores latest to the

intended head. No source changes since 0.1.1 — the published artifact for

0.1.2 is byte-equivalent to the 0.1.1 already on next.

  • Updated dependencies [415e689]
  • @takeoff-design/tokens@0.1.2
@takeoff-design/tokens0.1.2
Patch Changes
  • 415e689: Refresh release to recover the latest dist-tag.

The 0.1.1 release stalled in an inconsistent state: the auto-snapshot step

published 0.1.1 (instead of 0.1.1-next-<sha>) to the next dist-tag, and

when the Version Packages PR for 0.1.1 was merged the stable publish was

skipped with "version 0.1.1 is already published." That left latest pointing

at 0.1.0 while next pointed at 0.1.1.

This bump publishes both packages as 0.1.2 and restores latest to the

intended head. No source changes since 0.1.1 — the published artifact for

0.1.2 is byte-equivalent to the 0.1.1 already on next.