Frontile v0.18 is a major release with several breaking changes to theming, colors, DOM anatomy, and component APIs. This guide gives an overview of all of them and links to a detailed migration guide for each.
Two of these changes stop your app from working. The rest are cleanup, and one of them is optional for the whole 0.18 line.
| Change | If you skip it | Fails how? |
|---|---|---|
Missing @import "@frontile/theme" |
No Frontile styles at all | Loudly — the app is visibly unstyled |
Numbered color classes (bg-primary-500) |
Those elements render unstyled | Silently |
bg-background → bg-surface-canvas |
Those elements render unstyled | Silently |
--frontile-* variable references |
The declaration is dropped | Silently |
Nested LayoutTheme config |
Build or type error | Loudly, and only if you customize the theme |
@frontile/* package imports |
Nothing — they still work in 0.18.x | Deprecation warning only |
@frontile/forms-legacy / @frontile/changeset-form |
Nothing — they still work in 0.18.x, but are removed in 0.19.0 | Deprecation warning only |
| Derived border-radius scale | Slightly rounder corners on menus and small marks | Visual only — nothing to fix |
| Multi-select renders chips | Multi-selects look different — selections become removable chips | Visual only — nothing to fix |
| Filtered lists are ranked | Autocomplete/Select list the closest match first instead of source order |
Visual only — nothing to fix |
text-body-pico/-nano/-micro |
Those elements render unstyled | Silently |
| Body text-scale font sizes corrected | Body text (xs through xl) renders larger than intended |
Visual only — nothing to fix |
data-fr-* / mismatched data-component / most data-test-id selectors |
Those selectors stop matching | Silently, if you select Frontile-rendered elements yourself |
The silent ones are the reason to take this in order. A class Tailwind can't
resolve produces no error, no warning, and no CSS — the element just renders
without the style you asked for. We hit this in Frontile's own documentation
during the 0.18 work: thirteen demos were shipping with bg-success-50 and
bg-warning-50, rendering with no background at all, and nobody noticed until a
linter went looking. Budget time for looking at the result, not just for the
find-and-replace.
Nothing else is verifiable until the theme loads. Add the CSS import, and update your config shape if you customize it.
Impact: required. Time: 15–30 minutes.
@import "@frontile/theme" to your app/styles/app.cssLayoutTheme moved from a flat to a nested structure (hoverOpacity becomes
opacity: { hover })--frontile- prefix; colors gained --color-See: Theme Configuration
One pass over your classes and custom CSS. Everything in this step fails silently, so verify visually as you go rather than at the end.
Impact: required, and touches every colored element. Time: an hour or two for a small app, a day or more for a large one.
50, 100, … 950) become named levels (subtle, muted,
soft, mild, DEFAULT, firm, strong, bolder)default-* becomes neutral-*bg-background becomes bg-surface-canvas{color}-foreground and contrast-1/contrast-2 become on-{color}-{level}text-foreground is gonetheme-inverse flips every semantic token in a region, for panels that should read as the opposite themeColors also moved from HSL to OKLCH. That part is automatic — you may notice small perceptual differences, but there is nothing to change.
See: Semantic Colors
Seven @frontile/* component packages became the single frontile package. The
old packages still re-export everything and only log a deprecation warning, so
your imports keep working for all of 0.18.x.
Leave this until the app builds and looks right. It rewrites every Frontile import in your codebase, and doing that first buries the changes above in a diff you can't read — which matters precisely because those changes fail silently.
Impact: none until 0.19. Time: 10–30 minutes, mostly automated.
@frontile/forms-legacy and @frontile/changeset-form are deprecated and,
unlike the wrapper packages above, are being removed entirely in 0.19.0, not
just re-exported. If you depend on either, migrate to the modern frontile
forms (Form + Field pattern with Valibot, Zod, or a custom validator).
Impact: none until 0.19, unless you already depend on one of these packages. Time: varies with form count and validation complexity — see each guide's migration checklist.
See: Forms Legacy Migration Guide , Changeset Form Migration Guide
Autocomplete and filterable Select used to filter with a case-insensitive
"contains" check and render whatever survived in the order you passed it.
They now score each option and list the closest match first, and additionally
match acronyms (nz finds "New Zealand").
Nothing that matched before stops matching, so there is nothing to fix unless
you pass your own @filter — which now also accepts a score:
filter?: (itemValue: string, inputValue: string) => boolean | number;
See: Filter Ranking
Select with @selectionMode="multiple" used to show its selections as a
comma-joined string inside the trigger. It now renders each selection as a
removable Chip, so users
can drop one selection without reopening the dropdown. The control grows taller as chips
wrap, and a chips field is deliberately the same height as a same-size single
select (46px at md).
Nothing breaks, but the field is taller and looks different. If a layout depends on the old fixed-height appearance, opt back out per-select:
<Select @selectionMode='multiple' @selectedItemsDisplay='text' />
Chips inherit the Select's @intent and default to the faded appearance;
@chip={{hash appearance='outlined' size='md'}} tunes appearance, intent,
size, radius and withDot. @allowEmpty defaults to false, so the final
selection's chip renders with no close button; @isClearable clears
everything and ignores @allowEmpty. Chip close buttons are deliberately not
in the tab order — the combobox is the single tab stop, and Backspace on
the field removes the last chip in both filterable and non-filterable modes.
See the Select docs
for the full section.
Impact: visual only. Time: none required; a few minutes if you want to opt out.
Every component now carries a stable data-component/data-part DOM
anatomy. The old data-fr-* attributes are gone, several mismatched
data-component values were corrected, and most data-test-id attributes
that existed only as anatomy selectors were replaced. This only affects you
if your own CSS, querySelector calls, or tests select Frontile-rendered
elements directly — the public component API (@classes, yielded blocks,
etc.) is unchanged.
Impact: required only if you select internals directly (silent — the selector just stops matching); otherwise none. Time: a few minutes to a couple hours, depending on how many selectors your app has.
See: DOM Anatomy Attributes Migration , Customizing Component Styles for the ongoing contract.
@appearance → @variant, @intent → @color/@status — required only if you set themBoth styling axes are renamed. @appearance becomes @variant with a shared
value vocabulary (solid, soft, subtle, outline, ghost, plain), and
@intent becomes @color — or @status on Alert, NotificationCard and
FormFeedback, the three where the value also selects an icon, an ARIA role, or
whether a message is announced assertively. default becomes neutral
throughout; on Alert and NotificationCard, info folds into primary.
On the components whose API shipped in v0.17.1, the old props still work through
0.18.x and log a deprecation warning; they are removed in v0.19.0. Components
added during the 0.18 pre-release cycle are renamed outright with no warning —
if you tracked a 0.18.0-alpha.*/beta.* build, read that guide's second
section.
Impact: required only if you pass @appearance or @intent today;
otherwise none. Time: a few minutes to an hour, depending on how many call
sites you have.
See: Component API Naming Migration
The --text-body-* tokens were mapped to the wrong steps of the modular scale,
so xs through xl rendered larger than the design spec (e.g. md shipped at
20.74px instead of 16px). These now match spec, and the scale gained 4xs,
5xs, 2xl, and 3xl sizes to fill it out.
The non-standard text-body-pico, text-body-nano, and text-body-micro
tokens are gone — the body scale now uses the same 5xs…3xl naming as every
other text-style category. Replace them with the equivalent standard size,
which renders at the same pixel value:
| Removed | Use instead |
|---|---|
text-body-pico |
text-body-4xs |
text-body-nano |
text-body-3xs |
text-body-micro |
text-body-2xs |
Impact: required only if you use text-body-pico/-nano/-micro
directly (silent — the class stops resolving to any style); otherwise visual
only, from the corrected sizes. Time: a few minutes; search your codebase
for text-body-pico, text-body-nano, and text-body-micro.
See: Typography
@import "@frontile/theme" added to app.cssLayoutTheme config nested, if you customize it--frontile-* variable references renamed (colors take --color-)default-* renamed to neutral-*bg-background replaced with bg-surface-canvas{color}-foreground / contrast-* replaced with on-{color}-{level}frontile (optional until 0.19)@frontile/forms-legacy / @frontile/changeset-form, if used (required before 0.19)text-body-pico/-nano/-micro with text-body-4xs/-3xs/-2xsdata-fr-*, mismatched data-component, or retired
data-test-id selectors with the new data-component/data-part
attributes (see
DOM Anatomy Attributes Migration
)@appearance replaced with @variant, and @intent with @color —
or @status on Alert, NotificationCard and FormFeedback — with
default replaced by neutral (see
Component API Naming Migration
)Starting fresh on v0.18 needs no migration. Follow Getting Started .