Skip to content

v0.9.0

Choose a tag to compare

@joshstovall joshstovall released this 03 May 18:25
· 23 commits to main since this release

New Features

Slot props for inner Text and View elements

Most components now accept *Props overrides for the inner <Text> / <View> slots they render. Consumers can pass through any TextProps / ViewProps (style, weight, font family, accessibility, etc.) without rewriting the component or fighting children-prop precedence.

  • Label slots (labelProps): Accordion (titleProps), Avatar, Badge, Breadcrumbs (labelProps + separatorProps), Button, Checkbox, Chip, Indicator, Pagination (labelProps + activeLabelProps), Radio, Slider, Stepper, Switch, Tabs, Tooltip
  • Title / body slots (titleProps / bodyProps): Dialog, Notice, Toast
  • Description slots (descriptionProps): AutoComplete, Avatar, Checkbox, Input, Radio, Stepper, Switch, TimePicker
  • Section / control slots: AutoComplete + Input + TimePicker (startSectionProps, endSectionProps), Spoiler (controlProps), DataTable cell text customization
  • mergeSlotProps is now exported from @platform-blocks/ui so custom components can adopt the same merge semantics (theme defaults → slot props override → explicit props win).

useDisclosure, useDebouncedValue, useDebouncedCallback, useMediaQuery, useHover

Five hooks promoted to the public API. All have tests, demos, and metadata.

import { useDisclosure, useDebouncedValue, useMediaQuery } from '@platform-blocks/ui';

const [opened, { open, close, toggle }] = useDisclosure(false);
const [debounced] = useDebouncedValue(query, 200);
const isCompact = useMediaQuery('(max-width: 640px)');

Slider variants

<Slider> and <RangeSlider> accept a new variant prop with six visual treatments:

Variant Description
default Standard track, filled active range, solid thumb
filled Thicker, fully opaque inactive track (iOS-style)
outline Transparent track + border; active range is a colored fill
minimal Hairline track and a smaller, flatter thumb for dense UIs
segmented Track is divided at tick boundaries; active region fills whole segments
unstyled Strips all chrome — consumer styles via trackStyle / thumbStyle

Variant size multipliers are applied at the consumer level so the thumb stays visually aligned with the track ends.

RadioGroup variants

<RadioGroup> accepts a variant prop with four layouts:

Variant Description
default Classic stacked radio dots with labels
card Each option is a bordered, padded surface; selected card gets a colored border, tinted background, and a check icon — ideal when options have descriptions
segmented Joined buttons with shared borders, like an iOS/macOS segmented control (always horizontal)
chip Compact rounded pills that wrap onto multiple lines — great for filters and tag pickers

orientation is honored only by default; segmented is always horizontal and chip always wraps. Keyboard navigation (arrow-key cycling) and accessibilityRole="radio" are preserved across all variants.

Card.Section

Card gains a new <Card.Section> sub-component for full-bleed banners, dividers, and banded rows. Position-aware: the parent walks its children and tags the first/last sections so they only negate the relevant edges.

<Card padding={20} withBorder>
  <Card.Section>
    <Image source={hero} />
  </Card.Section>
  <Text>Body content keeps the parent padding.</Text>
  <Card.Section withBorder>
    <Text>Footer with a top divider.</Text>
  </Card.Section>
</Card>

Card also gains withBorder, borderColor, borderWidth, bg (palette name or CSS color), and testID. padding is widened from number to SizeValue (token or pixel).

Input variants and slot overrides

BaseInputProps exposes a variant of 'default' | 'filled' | 'outline' | 'unstyled', plus placeholderTextColor, labelProps, descriptionProps, startSectionProps, and endSectionProps. The variant set propagates to every component that extends BaseInputProps (Input, NumberInput, AutoComplete, TimePicker, etc.).

Divider enhancements

Divider is no longer just a line — it now supports:

  • variant: 'solid' | 'dashed' | 'dotted' | 'gradient'
  • colorVariant: 'border' | 'subtle' | 'muted' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray'
  • label (centered, start, or end content rendered in the middle of the line) + labelProps
  • opacity shorthand
  • size accepts a token or pixel thickness

Block bg shorthand and Text c / ff shorthands

  • <Block bg="primary"> resolves to the palette's shade-1 tint; <Block bg="primary.6"> picks an explicit shade; theme background keys ('surface', 'subtle', 'elevated', 'base') are also supported.
  • <Text c="dimmed"> for color shorthand and <Text ff="mono"> for font-family shorthand.

Demo coverage

36 new demos shipped across the UI package, including: label-customization / title-customization / text-customization for the components that gained slot props, Slider/variants, Radio/variants + Radio/theming, Card/sections + Card/border-and-bg, Input/variants + Input/slot-styling, Divider/gradient-opacity, Block/bg-shorthand, and Text/c-shorthand + Text/ff.

Improvements

DataTableFilter is now publicly typed

DataTableFilter was already exported from ./components/DataTable but missed the public re-export at the package root. It's now reachable via import type { DataTableFilter } from '@platform-blocks/ui'.

Playground exposes RadioGroup, not just Radio

The Radio playground entry now mounts <RadioGroup> (matching the convention used by Tabs, Select, and SegmentedControl — the playground exposes the parent/group rather than the inner element). Variant and orientation are surfaced as live segmented controls.

Slider playground covers variants

The Slider playground entry surfaces variant as a segmented control across all six values, with valueLabelPosition, valueLabelOffset, valueLabelAlwaysOn, and valueLabelAsCard pinned for live tweaking.

Fixes

Typecheck regressions cleared

The docs app + UI package now typecheck cleanly (down from 71 errors to 0). Touched areas:

  • Demo cleanup — removed references to props that no longer exist (mobileMode, touchOptimized, swipeToClose, floatingPosition, etc.) on TableOfContents; xxs size token replaced with xs; <Row wrap> boolean → wrap="wrap" string; <Column bg=…><Block bg=…> (Column doesn't accept bg); renderExpandedRowexpandableRowRender on DataTable.
  • Test infrastructureCard.testID (legitimately missing from CardProps) added; react-test-renderer types replaced with a structural shim so the Divider rendering test compiles without @types/react-test-renderer; renderHook callbacks for the debounce hooks now annotate their props parameter so the generic infers correctly.
  • Wrong import path in apps/platform-blocks.com/utils/sourceCode.ts (../data/sourceCodeMap./sourceCodeMap).
  • Strictness fixes in NumberInput, Rating, Ring, and QRCode demos/tests (typed callback params, palette fallbacks, narrowed reduce).

RadioGroup no longer requires theme.backgrounds for the default variant

Variant-only color tokens (accentTint, surfaceColor, etc.) are now computed lazily and only when variant !== 'default'. The default radio-dot path doesn't depend on theme.backgrounds anymore, which fixes 10 Radio tests that mocked a slim theme.

Migration notes

This release is predominantly additive. A few demos that referenced removed TableOfContents mobile props will need to drop those props if you copied them — the props were never on TableOfContentsProps, so they were silently broken anyway. If you imported DataTableFilter from @platform-blocks/ui/components/DataTable, the type is now also re-exported from the package root.