Update dependency @nldd/design-system to ^0.10.0 #471
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "renovate/nldd"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
^0.8.92→^0.10.0Release Notes
NederlandseDigitaleDienst/design-system (@nldd/design-system)
v0.10.0Compare Source
Highlights
data-nldd-themeon<html>and buttons, selections, links, the focus ring, the hero and the badge take that color instead of lintblauw, in light and dark mode. The ribbon and the logo stay lintblauw.@nldd/design-system/favicon/geel/and so on.Added
@nldd/design-system/themes/geel.cssand setdata-nldd-theme="geel"on<html>.@nldd/design-system/themesholds all of them, for a site that switches without a reload. Lintblauw is the default and needs no file. A theme sets the accent scale, the tokens per role and, for cool colors, a gray with a hint of the theme color. The "Thema's" page in Storybook explains what each role does.@nldd/design-system/favicon/geel/favicon.svgand…/geel/touch-icon.png, and the same for every theme, have the field behind the ribbon in the theme color. The ribbon stays lintblauw and the coat of arms white. Without a theme,@nldd/design-system/favicon.svgandtouch-icon.pngstay as they are.--semantics-surfaces-accent-tinted-*is a tint of the theme color with the contrast oftinted.--semantics-surfaces-accent-filled-*is the panel of the hero, with--semantics-content-on-accent-filled-*for the text on it.background="accent-tinted"onnldd-box,nldd-cardand the page sections. The area takes the tint of the theme color.color="inherit"onnldd-checkbox,nldd-radio-buttonandnldd-switch, and on their fields. A checked control takes the text color of the area it sits on, and the color of that area for its mark. Use it on the accent panel of the hero, where the theme color would not stand out.--components-*layer, only for what a theme sets. A theme reaches:root, not the styles of a component, so the few values a theme changes for one component live invariables.cssas--components-{component}-*: the accent badge and status bar, the icon of the accent banner, the mention in the text editor and the current tab of the tab bar. It is not the old component layer: a component reads only its own tokens, and the style check fails on any other. Everything else stays a local variable or a semantics token.Changed
--semantics-controls-is-highlighted-*is now--semantics-controls-is-selected-*. It is the color of a checked, selected or current control, so the name says that, as--semantics-buttons-*-is-selected-*already did. The highlighted item of a menu or mention list, where the keyboard or pointer is, reads--semantics-list-items-is-highlighted-*instead, like the highlighted row of a list, and that token has its own values now. If your own CSS reads one of these tokens, rename it.--semantics-categories-*now. The banner draws on--semantics-surfaces-{tinted,accent-tinted,success-tinted,warning-tinted,critical-tinted}-*, new for success, warning and critical;nldd-box background="critical"takes a surface too. The badge, status bar, date picker and image keep their colors in their own styles, and a mention in the text editor reads--components-text-editor-mention-*, which a theme can set. The neutral and accent banner are one step lighter, on the same tint asbackground="tinted", and the warning and success icons of the banner are a lighter orange and green in light mode.referencecategory style is gone.--semantics-categories-{color}-reference-*drew the panel of the hero, which now reads--semantics-surfaces-accent-filled-*and--semantics-content-on-accent-filled-*. No component used it anymore. If your own CSS reads one of these tokens, take the accent-filled surface for the theme color, or thefilledstyle of the color.highlight-border, as everywhere else.--semantics-surfaces-border-widthis now--semantics-surfaces-highlight-border-width, and--semantics-surfaces-base-border-colorand--semantics-surfaces-tinted-border-colorare now-highlight-border-color. The components follow by themselves. If your own CSS reads one of these tokens, rename it.--semantics-controls-is-selected-mark-colorsets that color.nldd-top-navigation-barstands where the printed logo has it. On md and lg it is 10px from the ribbon instead of 8px, so the first letter is as far from the ribbon as in the logo. On a small screen a wordmark taller than the ribbon keeps 16px from the top instead of 12px.Fixed
40reminstead of640px), which scale with the zoom, and so are the thresholds of their own in rich text, the split sections, the progress bar, pagination and the document tab bar. The style check now fails on a px length in a query condition. At the default font size of 16px nothing changes; with a larger default font size the narrow layout comes earlier.@nldd/design-system/breakpointsaddsqueryBreakpoints(the rem values), andbreakpointPx()androotFontSize()for a width you measure in script;breakpointskeeps its px values. Ahide-beloworhide-abovewith a px value of your own still has the problem in Safari: write it in rem.nldd-herokeeps the full distance at the top again. Half the distance (8px) put the hero too close to the top bar on mobile, so there it is 16px again, as for the other page sections. On md and lg the top stays at half.Breaking
nldd-badgeno longer takes a Rijkshuisstijl color.coloris the same set asnldd-status-bar, plusinherit:critical(the default),accent,neutral,warning,successandinherit. A badge is a signal, not a category, and a color of its own clashed with the theme. A badge withcolor="hemelblauw"or another named color now gets the accent, and says so in the console during development. Pick the color for the whole site with a theme, or usecustom-colorfor a color of its own.nldd-herono longer takes a Rijkshuisstijl color for its panel.main-backgroundisaccent(the default) orbase. A color per hero came before themes. A theme now picks the color for the whole site. A hero withmain-background="donkerblauw"or another named color gets the accent instead, and says so in the console during development. To keep the color, set the theme on<html>(data-nldd-theme="donkerblauw", with@nldd/design-system/themes/donkerblauw.css) and remove the attribute. Without a theme the accent is lintblauw.v0.9.0Compare Source
Highlights
href. Nobody has to set anything, so nobody can forget it.nldd-sidebar-sectioncloses the sheet and moves focus to the heading it points at, in view.accessible-labelworks the same everywhere. It names a navigation list, andnldd-list-itemhas one too.nldd-step-cellputs its dot at the top of a row withvertical-alignment="top", or beside a first line of any height withmarker-offset, so a step with a title and a paragraph no longer has its dot halfway down the text.Added
A hint for a link to another website. An
hrefto another origin than the page adds a visually hidden "andere website" to the link's name, in one sentence with "opent in nieuw tabblad" when both apply: "Lees de startnotitie, andere website, opent in nieuw tabblad". It works innldd-button,nldd-icon-button,nldd-link,nldd-card,nldd-avatar,nldd-toolbar,nldd-list-itemandnldd-list-item-segment. Onlyhttp:andhttps:links count, a subdomain counts as another website too, and there is no way to switch it off: another domain has its own heading, navigation and title. Anexternal-linkicon stays your own choice. The hint is translated in English, Frisian and Papiamento.A design guideline: don't explain the interface, improve it. A sentence that says where to click or what a button or filter does shows that the interface does not say it by itself. People read it only once they get stuck, and it goes stale as soon as the interface changes. Change the label, the order or the component instead. Where a hint is still needed, keep it short and put it next to the part it is about, not in an introduction above.
accessible-labelonnldd-list-item, the name of the row's action. For a row whose text does not say enough on its own. It replaces the content of the whole row for a screen reader, the supporting text and a status included, so set it only on a row without text of its own, or put everything that matters in it.vertical-alignmentandmarker-offsetonnldd-step-cell, for a step in a row higher than one line. The dot stood in the middle of the row, so beside a title with a paragraph under it, it hung halfway down the text.vertical-alignment="top"puts it against the row's top padding andbottomagainst its bottom padding, as a whole dot would stand, so aminordot lines up with amajorone. To line it up with what stands beside it, such as the first line of a text cell or annldd-title,marker-offsetsays how far below the top of the content the middle of the dot stands, measured after the row's padding, as a length or as a percentage of the content's height. Write it inrem, so the dot moves along when someone sets a larger font size. The track runs to the middle of the dot, so rows still connect.Changed
The hint for a new tab follows the name after a comma, in lowercase. "Lees de startnotitie, opent in nieuw tabblad". The hidden text ran on without a pause, and the hint in an
aria-labelstarted with a capital after the comma.nldd-list-item-segmentnow gives the hint as well.A link in the sheet of
nldd-sidebar-sectioncloses it and moves focus to the place it points at. Left to the browser, the page scrolled under a sheet that was still open, and closing it gave focus back to the trigger without scrolling to it, so focus ended up out of sight. The section now closes the sheet, scrolls to the heading and gives it focus with a temporary tab stop, asnldd-skip-linkdoes. A link to another page, in a new tab, with a modifier key or into the sidebar itself is left alone, as is a click you already handled withpreventDefault(). The content page pattern no longer needs its own click handler.accessible-labelnames a navigation list. Withtype="navigation"it did nothing, and the landmark kept the default name "Navigatie". It now names the landmark, as it names the list and the search field in the other types. Anaria-labeloraria-labelledbyon the element still wins. The page patterns useaccessible-labelon their navigation lists.The corner of an
nldd-herowithout media is smaller. On a hero with a single title, the radius of 60px took up more than 80% of a panel 73px high. Without an image the panel is the whole hero, so it now takes the radius of a panel: half, 0.75X on small screens and 1X above. In any hero the radius is never more than a third of the shortest side of the area it rounds, which also protects a hero with a smallmedia-height. Without media the corner applies in every layout, overlap included, and the hero chooses top-right, or top-left for a panel on the right.Breaking
components.{name}.opens-in-new-tab-labelis nowcomponents.{name}.link-hint-new-tab-lowercase-label, next to the newlink-hint-external-lowercase-labelandlink-hint-external-new-tab-lowercase-label, so the three hints of a link sit together. The text always follows a name after a comma, so the default is lowercase: "opent in nieuw tabblad". If you override this text withtranslationsorsetTranslations(), rename the key and write the text in lowercase. The old key does nothing anymore.v0.8.100Compare Source
Highlights
nldd-herocan take the rounded corner of the Rijkshuisstijl.cornerrounds one corner of the image and half a one on the panel, in a contained hero with the panel in a corner or along a side.nldd-titletakessm-,md-andlg-custom-sizefor a statement larger than size 1, set per breakpoint and never smaller than size 1.nldd-icon. Search every icon and alias there, or see what is new or updated.Added
item-max-widthonnldd-collection, the largest width of an item in a horizontal scroll. A scroll with only one or two items, such as an agenda that starts small, keeps them at a readable width instead of filling the container. It takes a CSS length such as400pxor25rem, is never narrower thanitem-min-widthand never wider than the container. Once the items overflow, scrolling, the edge fade and the previous and next buttons work as before. Grid, lanes and stack ignore it. A grid leaves empty columns rather than stretching an item, so the problem does not occur there, and a maximum would change how many columns fit.A design guideline for link text: say where the link goes, no "click here". A screen reader can read every link on a page as a list of its own, and people scanning a page read the underlined words. Out of context, "klik hier", "lees meer" and "meer info" say nothing. The guideline asks for the destination or subject as the link, the distinguishing words first, a different text for every destination, and the format and size of a file. WCAG 2.4.4 accepts a generic link when the sentence around it gives the context. This guideline is stricter, because that context only helps whoever reads the sentence. The examples in Storybook follow it now.
corneronnldd-hero, the rounded corner of the Rijkshuisstijl shape language.<nldd-hero corner>(orcorner="auto") rounds one corner of the image and lets the hero choose one the panel leaves free;corner="top-left"and the other three choose it yourself. The panel gets a corner at half the radius, the one pointing into the image, and stands against the edge of the image, so amain-widthof 33.33% or 50% is that share of the image. The radius follows the ribbon width: 1.5X on small screens, 2X above. It applies withlayout="contained"and the panel in a corner or along a side; with overlap or a centered panel the hero stays rectangular and says so in development, as it does for a chosen corner that sits under the panel or a corner withwidth="full". Withoutcornerthe hero is rectangular, as before.sm-custom-size,md-custom-sizeandlg-custom-sizeonnldd-title, for a statement larger than size 1. Size 1 tops out at 52px, which is small for a title that has to make an impression. These take any CSS length per breakpoint, with the weight, typeface and line height of size 1. They never go below size 1 at the same breakpoint: a smaller value shows as size 1 and says so in development, so they are no way around the scale for an ordinary heading. There is no plaincustom-sizeon purpose: a breakpoint you leave out keeps the size fromsize, so a title made large for a wide screen does not break a narrow one.no-resultsonnldd-table, for a table whose rows are all filtered away. An empty table and a filtered one said the same thing, because the table had onlyempty. When every body row is[hidden], the table now shows[slot="no-results"]: say that nothing matches and how to get rows back, and keepemptyfor a table that has no rows yet. Withoutno-resultsit falls back toempty, the same asnldd-list.Changed
nldd-modal-dialogsays so in development when it opens with one action. A dialog with one button offers nothing to choose, which the design guidelines advise against. Tell the user with annldd-notificationor an inline message instead, or give the dialog a real choice. It warns once per dialog.The component docs point to the design guidelines where an attribute invites misuse.
disabledandloadingon the buttons,nldd-dropdownas a last resort after visible options and a combo box, when annldd-modal-dialogis right, and horizontal scroll innldd-collectionas more than a carousel. The examples in Storybook follow the guidelines as well: labels in the imperative, no "Annuleer" next to the primary action, the safe way out first in a destructive dialog, and Dutch text with the fixed example cast. They are built from the system's own components too:nldd-containerfor layout,nldd-textfor captions with annldd-spacerto the example, and cells inside annldd-list, so the code you copy from Storybook has no inline layout CSS.The icon gallery has its own page, Docs › Iconen. It was the last story of
nldd-icon, where nobody looking for an icon name thought to look. The page shows every icon and alias, two across on a phone, with a search field and a filter for the new and updated ones.Inline elements in
nldd-textlook as they do innldd-rich-text.<em>is italic,<a>an underlined link,<code>sits on a tinted surface in the monospace font and<mark>is highlighted, beside the bold<strong>it already had. A short text with a code word or a link no longer needs a rich-text block.The form pattern allows a format requirement on an optional field. An optional field is not
required, and an empty value passes every requirement, so a phone number you may leave out can still be checked when it is filled in.The title of the wordmark in
nldd-top-navigation-baris semi-bold. It was medium, close enough to the subtitle below it that the two read as one block. One step heavier sets the name apart without competing with the page title.A menu under an
nldd-menu-bar-itemopens against its indicator. The indicator stops 6px short of the item's bottom edge, and the menu hung off the item, so it opened with a gap below the gray box that marked the open item. It now touches the indicator, which keeps its rounded corners.Breaking
Items in a horizontal-scroll
nldd-collectionstop at 480px. One or two items that fit grew to the full width of the container, so a card of 320px became more than 1000px wide on a desktop. They now grow up to 480px and stay at the start. Anitem-min-widthwider than 480px raises the limit with it, anditem-max-widthsets your own;item-max-width="100%"brings back the old behavior.item-widthonnldd-collectionis nowitem-min-width. It was a minimum in every layout: in a grid the narrowest a column gets, in a horizontal scroll the width an item never shrinks below. The new name says so, and pairs withitem-max-width. There is no alias. Anitem-widthleft on a collection does nothing, so its items fall back to the default of 280px; in development the collection says so once, with the renamed attribute. Rename the attribute on everynldd-collection.Fixed
nldd-container width="fit-content"takes the width of its content. The container measures its own width for its breakpoint padding and gap, and that measuring left a fit-content container 0px wide, so whatever was in it piled up on its neighbours. A fit-content container no longer measures itself; its breakpoints follow the container around it.v0.8.99Compare Source
Highlights
nldd-menucan be a navigation. Withvariant="navigation"it is a list of links instead of a menu of actions, so a submenu in the site navigation is read as what it is.nldd-tab-barputs the tabs that do not fit behind a "Meer tabs" button instead of cutting off every tab, and the current tab always stays in the bar. In annldd-toolbarthe other items make room first.nldd-document-tab-barkeep their width while you close them, and grow into the space once the pointer leaves the bar.NLDDNotification.show()creates, shows and cleans up a notification, which is the way to raise one from a framework.nldd-visually-hiddenis new, for text that only a screen reader reads, such as the header of a column of checkboxes.Added
variant="navigation"onnldd-menu, for a submenu in a site navigation. A submenu innldd-top-navigation-barwas a menu of actions:role="menu"with menu items. For site navigation that is the wrong role, since a screen reader switches to an application mode in a menu where a visitor expects a list of links. Withvariant="navigation"the menu is a list (role="list") and each item a list item around a real link; it looks the same. Tab reaches every link, the arrow keys, Home and End move between them, Escape closes and returns focus to the trigger, and the menu closes when focus leaves it. Thenldd-menu-bar-itemthat opens it saysaria-expandedand no longeraria-haspopup="menu". A submenu and a group inside it follow along. Checkbox and radio items, shortcuts andfilter()belong to actions and warn in development here. Give every item anhref, and keep the default variant for menus that do things, such as an account menu or a language switch.NLDDNotification.show(), to raise a notification from code. Annldd-notificationplaces itself, which is why it does not matter where you write it. It also means a framework cannot render one: the element is no longer where the framework left it, so a keyed list (v-forwith a:key) fails on its next render and a removed row leaves its notification on screen.NLDDNotification.show({ text, supportingText, variant, icon, duration, actions })creates the element, shows it and removes it once it is dismissed. An action is{ text, onClick }, at most two, and a click on one takes the notification away as well. It returns a handle withdismiss()andupdate(options). An update changes the message, puts the notification in front and starts its clock over, so a status that changes often stays one notification, and the clock still pauses on hover and focus. The declarative element stays as it is; raise notifications withshow()from a framework.nldd-visually-hidden, text for a screen reader only. It stays in the accessibility tree and the reading order and takes no room on the page. Use it for a name that is visually redundant, such as the header of a column of checkboxes. The recipe is the one the components already use inside, so a site no longer needs a.visually-hiddenclass of its own. It is not for repairing structure that is missing, nor for a status: a status that matters to a screen reader matters to anyone, so show it.Changed
Tabs in
nldd-tab-barthat do not fit go to a menu instead of being cut off. On a narrow screen every tab was cut off with an ellipsis, and the rest of its text could not be read anywhere, often the very part that told the tabs apart. Tabs that do not fit now go behind a "Meer tabs" button at the end of the bar, in a menu. The current tab always stays in the bar. While tabs are in the menu, the bar fills its container and the tabs in it share the width in proportion to their own; choosing a tab in the bar leaves the rest where it is, and one chosen from the menu swaps places with the last tab in the bar, so the bar does not jump when you switch. In annldd-toolbarthe other items go to the toolbar's menu first; when it is the tab bar's turn, it narrows down to the current tab beside its own button, and only then goes into the toolbar's menu as a whole. Tabs are hidden, never moved in the DOM, so a framework that renders them finds them where it put them. Only a tab that does not fit on its own is still cut off. The label of the button can be translated withcomponents.tab-bar.overflow-action.nldd-code-viewerscrolls across its whole box. The padding sat on the box and the scrolling on the code inside it, so a long line stopped short of the edge, the scrollbar was inset, and the room for the copy button kept the whole right side free. The scroll area is now the box itself, with the padding inside it: a line scrolls out to the edge, the scrollbar runs from corner to corner, and at the end of a long line the last character stops beside the copy button instead of behind it.Closing tabs in
nldd-document-tab-barone after another keeps the next close button under the pointer. Closing a tab with the pointer used to spread the others over the space at once, so the close button of the next tab moved away from the pointer. The tabs now keep their width until the pointer leaves the bar, and the next close button slides in under the pointer. Then they grow into the space, without motion for anyone who asks for reduced motion. Closing the last tab of the row, with none left in the overflow, lets them grow into the space at once; closing from the keyboard spreads them at once.Breaking
labelonnldd-toolbaris nowaccessible-label. It was the one place where an accessible name was calledlabel; every other component saysaccessible-label, and onnldd-toolbar-itemlabelis the visible text below the item, which made the two easy to mix up. There is no alias: alabelleft on a toolbar does nothing, so the toolbar loses its name. Rename the attribute on everynldd-toolbar. Thelabelonnldd-toolbar-itemstays as it is.Fixed
nldd-popoverunder a button can be selected.nldd-button,nldd-icon-button,nldd-menu-bar-itemandnldd-menu-barswitch text selection off for themselves and everything in them, and a popover in theirpopupslot is in them. The popover now switches it back on, so its text can be selected and copied.nldd-cardcame from:active, which can flash or linger while a touch scrolls the page. The card now sets it on a press and clears it on release, or when the browser hands the touch to scrolling, asnldd-list-itemalready did.nldd-top-navigation-barstarts at the top of the bar again withlogo-href. With a link on the logo the ribbon sat in a span as an inline image, on the text baseline with room below it for descenders, and centering that taller box lifted the ribbon 2 to 4 pixels above the bar. Anything placed above the bar, such as annldd-status-bar, was overlapped by it.nldd-top-navigation-barhasaria-current. Behind the Menu button on a narrow screen the current item was only painted. The list in that sheet is a navigation list now, named after the global menu bar, so the link of the current item carriesaria-currentwith thecurrent-typeof its menu bar item.v0.8.98Compare Source
Highlights
nldd-list type="navigation"goes in its parent row'sslot="children"and is then a list inside a list item, where it used to be an indented row in one flat list.nldd-validation-listno longer rejects an empty optional field. Amatchorminlengthunder a field that may stay empty used to block every submit that left it empty. See Breaking if a field relies on the old behavior.Added
Sub-pages in
nldd-list type="navigation". Put the rows of a sub-level in their parent row'sslot="children", the slot a tree already uses. The parent renders them as a list inside its list item, so a screen reader hears the level and how many there are; an indent made with a spacer cell alone stayed one flat list. They are always shown and the arrow keys walk them like any other row. Indenting is still yours: add annldd-spacer-cellper level.max-heightonnldd-code-viewer. A CSS length that caps the block, for a long file in a frame of its own: the viewer grows up to it and scrolls vertically beyond it. There was no supported way to do this, short of a stylesheet in the shadow root.Changed
nldd-menu-bar-itemthat shows only its icon has a tooltip. Withicon-only, or withcontent-priority="icon"in a compact bar, the text was gone for anyone not using a screen reader. A bar is compact at 200% zoom on a desktop too. The text now shows on hover and focus.nldd-tooltipand nothing else. While a tooltip is showing, by hover or by focus, the first Escape closes the tooltip only. A dialog, sheet or menu around it stays open and closes on the next press, where one press used to close both. The tooltip takes the key in the capture phase ondocumentand stops it there, so a handler of your own for Escape, in a search field or a combobox for instance, does not hear that first press either.nldd-menu-bar. An item withhrefused to become a button in the "Meer opties" menu. It is an<a>now, so it opens in a new tab like the item in the bar, and a click handler on the original item still runs and can cancel the navigation.nldd-code-viewerstays on screen. "Kopiëren mislukt" disappeared after two seconds like the success message. It now stays until you leave the button, press Escape or try again. The button takes focus on click, so leaving it works in Safari too.Breaking
nldd-validation-listskip an empty value unless the field needs one. An empty value used to failmatch,minlengthandmaxlengthlike any other. It now passes them, the way the nativepatternandminlengthleave an empty input alone, unless an item hasrequiredor the control itself isrequired. If you counted onminlengthto stop an empty field, addrequiredto the control or add an item withrequired. For example:<nldd-validation-item id="password-required" required>Vul een wachtwoord in</nldd-validation-item>next to theminlengthitem.Fixed
nldd-skip-linkreaches its target, and Tab continues from there. Three things could leave it doing nothing. Withouthrefit moved focus to the next sibling whatever that was, so a<noscript>, a<script>or a hidden element right after it left the button dead; it now passes those over, and warns in development when nothing follows at all. In Safari the next Tab then left the page when the target was annldd-*component, because WebKit skips everything inside a shadow host withtabindex="-1". And with anhref, Safari kept focus on the link. The skip link now moves focus itself in both forms, so the target needs notabindexof its own.nldd-tooltipthat was opened by pointing at it. Escape only worked while focus was inside the tooltip's trigger, so with the pointer on a button and focus elsewhere it stayed open (WCAG 1.4.13).aria-currentin the overflow menu ofnldd-menu-bar. The item was marked visually, but a screen reader heard nothing.nldd-code-viewerthat scrolls vertically is reachable by keyboard. The scroller only became a tab stop with a role and a name when a line was too wide, never when the block was too tall, and never withwrap. It now measures both directions.nldd-sidebar-sectionhas a role and a name in Firefox. Firefox puts a tab stop on anything that scrolls, and that was an unnameddivinside the landmark. Theasideitself scrolls now.v0.8.97Compare Source
Highlights
nldd-herooverlaps the image. By default the text panel now falls over the bottom of the image and runs on below it, as far as its content needs. The image keeps a fixed height and sets the mood, the panel carries the page, and a long text no longer has to fit inside the photo.nldd-app-viewis nownldd-shell. It is the outer layer of a website as much as of an application, and the old name suggested only the second. Every page needs the rename, since there is no alias; see Breaking.heading-1throughheading-6,numbered-list,square-1,bold,strikethrough,underlinedandtext-format-size.Added
layoutonnldd-hero, withoverlap(the default) andcontained. Inoverlapthe text panel falls over the bottom of the image byoverlap-size(any CSS length, 160px by default) and runs on below it as far as its content needs. The image keeps a fixed height (320px on md, 400px on lg, or your ownmedia-heightormedia-aspect-ratio), so it stays the same whatever the content, and the overlap stays the same whatever the image. A panel shorter thanoverlap-sizeends inside the image; that is up to you, and it says so in development.containedkeeps the panel on the image, in any of the eight positions.iconslot onnldd-menu-bar-item, for something other than an icon in that place: annldd-avatarfor the person who is signed in, for instance. It gets the size of an icon and followscontent-priorityandicon-onlyas the icon does, so withcontent-priority="icon"only the avatar is left on a narrow screen and the text becomes the accessible name.media-heightonnldd-hero, a fixed height for the image in any CSS length, next tomedia-aspect-ratio. It wins over the ratio.vertical-alignmentonnldd-one-half-one-half-section,nldd-two-thirds-one-third-sectionandnldd-one-third-two-thirds-section:top(the default),centerorbottomplaces the shorter column next to the longer one, such as text beside a taller image. It only applies while the columns stand side by side; stacked, they follow each other.growon every page section, to choose which section takes the height a page has left. Until now that was always the last section, so the footer sat at the bottom of a short page; withgrowyou can let a hero or a middle section fill instead. Withoutgrownothing changes.nldd-pagemarks the growing section withdata-growing, a state it derives and never writes back intogrow.page-structure.mdinnldd-design-build), for the frame every page of a website shares: thenldd-shellaround everything, one section per block of content, a surface made withbackground, and oneh1. Its header is the bar on top: search and a language menu in the utility slot of the top bar, a back button to the page above on every page but the home, and a skip link around it. Where a site has a personal part, a third item signs you in, and becomes a menu with your name, your profile and a way to sign out once you have. A status bar, when there is something every visitor has to know, goes above the bar. Its footer has the breadcrumbs in a row of their own, a main part each website fills in itself, and a legal bar that links straight to Contact, Toegankelijkheid, Privacy and Over deze website: grouping the privacy statement under an "about" page alone is not enough, since the European data protection guidelines ask for a direct link on every page.topic-page.md,navigation-page.mdinnldd-design-build). A topic page opens with a hero holding a short explanation and its one main task, then has text beside an image, link cards to pages wherever they already live, a list of the pages below the topic, and related topics as link cards in a tinted section at the bottom. The home page shows the same kinds of sections: an image beside text in annldd-one-half-one-half-section, cards with an intro, and a list of topics beside its title and a short text, laid out like the navigation page. A navigation page shows its choice as a navigation list next to the title and a short intro, in annldd-one-third-two-thirds-section, or as a full-width collection of link cards below them; the pattern shows both.nldd-list-itemandnldd-list-item-segmenthavecurrent-type, thearia-currentvalue acurrentrow or segment puts on its link. It was alwayspage, which is wrong for a table of contents: every row there points at a section of the page you are already on. Setcurrent-type="location"there. Same attribute and values as onnldd-menu-bar-item.Breaking
main-widthonnldd-herofollows the content.1/2,2/3and3/4are gone: half the width was too narrow for text on md, and three quarters too wide to read on a large screen. The panel is now as wide as its content needs, between 480px and 640px and never wider than the image allows (main-width="auto", the default).fullstays, and any CSS width (560px,60%) sets it exactly, still never wider than the image allows. A fraction is not a CSS width, so it falls back toautoand says so in development.nldd-heroused to keep its text panel on the image; it now lets it fall over the bottom edge and run on below, and takes only the horizontal side ofmain-position. A hero whose panel stands at the top or along a side (top-left,leftand so on), or whose panel is too short to reach below the image, needslayout="contained"to stay as it was. A hero without an image does not change.nldd-app-viewis nownldd-shell. It is the outer layer of everything, a website as much as an application, and "app view" suggested it was only for the second. Rename the tag in every page, the import (@nldd/design-system/app-viewbecomes/shell) and the class (NLDDAppViewbecomesNLDDShell). There is no alias: annldd-app-viewleft behind is an unknown element, so the page loses its background and its scroll mode, and the document font, which the stylesheet sets once a shell is on the page. A CSS selector onnldd-app-viewneeds the new name too.data-widthinnldd-rich-textis nowdata-rich-text-width. The attribute that moves one child to another width zone (main,wideorfull) said neither what it belonged to nor that it only works inside a rich text, and it read like thewidthof a page section, which is something else. There is no alias: adata-widthleft behind does nothing, and the child falls back to the zone of its kind.is-lastis gone from page sections.nldd-pagemarks the section that takes the leftover height withdata-growinginstead of theis-lastclass, since a framework's class binding rewritesclassand could wipe it. If you styled.is-lastyourself, use[data-growing].Changed
nldd-herono longer usesheight. On the other page sectionsheightis a minimum height for the whole section; on a hero it stretched the text panel once that sat below the image. The height of a hero comes from its image:media-aspect-ratio, or the newmedia-height. A hero givenheightignores it and says so in development.nldd-heroalways stands on the image. The image in a hero sets the mood and is never the main content, so the panel no longer sits against its edges or beside it. On md and lg it keeps a section gap (24 or 32px) from the edges, so it reads as a panel on the image rather than a frame cut out of it.main-width="full"used to put the image in a strip of its own above or below the panel; it is now a strip across the image, the same gap from the sides. On mobile the image comes first, also for a top position, and the panel follows below it, indented by 16px and overlapping the image by 48px.main-positiongainsbottom-centerandtop-center.nldd-container,nldd-sheetandnldd-imageno longer write their defaults into the DOM. Every section carriedwidth=""andbackground="inherit", the heromedia-aspect-ratio=""and an image without a sourcesrc="", whether or not anyone set them. They now only appear when they differ from the default, as the other attributes in the system already did. If you selected on one of those empty or default attributes, select on the value you set instead.nldd-text-cellandnldd-title-cellwrap withtext-wrap: pretty, like the main text of a text cell already did, so a description over two lines no longer ends on a single word.nldd-rich-textstays within the line of text. Everynldd-*component used to get the full width, so annldd-boxornldd-cardwith a sentence in it grew past the text around it. A component now reads at themainsize, like the text, unless it needs the room:nldd-collectionandnldd-containerkeep the full width.nldd-image,nldd-tableandnldd-step-barget thewideaccent, like other media and a plaintable; annldd-tableand annldd-step-barused to get the full width. Code reads atmaintoo: apreand annldd-code-viewerused to span the full width, however short their lines, and now fill the line of text and scroll inside their block when a line is longer. Other elements that are not text, such asdetailsorform, read atmaintoo. Setdata-rich-text-width="wide"or"full"on a child to give it more.nldd-rich-textkeep their size on every width. They used to grow with the screen, which made anh2on a wide screen more than twice the size of the text around it (41px next to 18px), so an article broke into blocks at every heading. They now keep the size they have on a phone everywhere (anh2is 29px). Page and section titles innldd-titlestill grow, so the step from a page title to the headings in the text is clearer than before.nldd-page, a section no longer grows just for being the last child; give itgrow.nldd-herosits closer to the top bar. Its top padding is now half that of the other page sections (8, 16 or 24px instead of 16, 32 or 48px). The full distance is meant to separate two sections, and right under the top bar, where a hero almost always sits, it read as a gap. The bottom keeps the full distance. Setpadding-topto place a hero elsewhere.nldd-imageshows where an image belongs. Without an image (nosrc,srcset,lqipor slotted media) it used to leave an empty gap, or nothing at all withoutaspect-ratio. It now shows a neutral area with an image icon, in the box, shape and caption a real image would get, and 16/9 when there is noaspect-ratio, or the box its parent gives it, such as the media area of a hero. A prototype or a pattern gets the right composition before there are photos, and an image someone forgot stands out instead of leaving a silent hole. While the built-in image loads without anlqip, the same area shows without the icon, and it goes once the image is in. Thelqipgradient is now called a loading preview in the docs, to keep it apart from this.heading-1throughheading-6have a new H and new digits,numbered-listandsquare-1have new digits to match, andbold,strikethrough,underlinedandtext-format-sizeare drawn again. The names stay the same.profileicon is a contact card.profileandaccountboth pointed at the person in a circle, while an account and a profile are two things that often stand one above the other in the same menu.profilenow gives the contact card (person-text-rectangle);accountkeeps the person in a circle. If you usedicon="profile"for the account itself, useaccount.nldd-status-barsays where it belongs. The bar is the first thing on the screen, abovenldd-top-navigation-barornldd-top-title-barand across the whole app. Its docs say so now, and below a top bar it warns in development. A top bar in a sheet or dialog does not count.nldd-sidebar-sectionno longer draws the eye. Beside the main content it was a tinted box, which pulled attention away from the text it sits next to. It now takes the surface of the section around it, base on a plain page and tinted on a tinted section, and is edged by a divider line, as the panes of a split view are. It still reads as a block, which it needs to be because it can scroll, and acurrentrow in a list inside it now stands out against it. The sheet on narrow screens is unchanged.content-page.md, with a hero, cards and a call to action, ishome-page.md. Its hero now holds the main goal as a button, and each card has a button that leads on.content-page.mdnow describes a page that explains one thing: the title as the heading, a table of contents in the sidebar of annldd-sidebar-sectionthat becomes a button under the title on narrow screens and marks the section you are reading, or, for a short page with fewer than three headings, no table of contents and the action beside the text, or no action at all, the text in a rich text with the action below it, and the breadcrumbs in the footer. The frame every page shares, with its header and footer, has its own pattern.Fixed
nldd-imagein the media slot ofnldd-herofills the image area. It took the height of the photo instead, so a portrait photo showed only its top, and a lazy image that had not loaded yet had no height at all, never came into view and never loaded. The hero now tells the image to fill its area, and the photo is cropped from the center.dist, twice, along with a page of color palettes. Over 1 MB that no consumer uses is gone from the package.nldd-shellwas defined started out on its own, and nothing made it register once the shell was: in Chrome and Firefox the document did not scroll, in Safari a sidebar did not stick, until you resized the window. That happens whenever a section is imported before the shell, sincenldd-sidebar-sectiondefinesnldd-page, and an app cannot control that order with code splitting and lazy routes. A page, a split view or a pane now waits for the shell, sheet or modal dialog above it to be defined and registers then.nldd-rich-textno longer runs out of the column. The safety net that breaks a word wider than the column was only on paragraphs and list items; headings have it too now. A heading still never gets a hyphen.nldd-spacer-cellbetween them, as in the other patterns.hreftakes its accessible name from its content. It does not: the link lies over the card as an empty anchor, so it needsaccessible-label. Every link card in the patterns now has one, set to the card's title.v0.8.96Compare Source
Highlights
Twelve new icons. Seven file icons for a document's format:
file-pdf,file-markdown, andfile-odt,file-ods,file-odp,file-odgandfile-odffor the OpenDocument formats, each also answering to{format}-document, such aspdf-document. Andmicroscope,share-network,power-plug-socket(alsoplug-connected) for a plug in its socket, andsubscriptandsuperscriptfor text formatting.nldd-step-barandnldd-step-cell. The step indicator and the timeline track cell are renamed after what they draw: steps, across and under each other. See Breaking.The
--components-*layer is gone. A component's values now sit as local variables in its own stylesheet, and what several components share became a semantics token. Restyling one component through a public token was a quiet way to make it differ from the rest. If you want a component to change, ask in an issue. See Breaking.Added
File icons
file-pdf,file-markdown,file-odt,file-ods,file-odp,file-odgandfile-odf, with the aliasespdf-document,markdown-document,odt-document,ods-document,odp-document,odg-documentandodf-document.Icons
microscope,share-network,power-plug-socket(with the aliasplug-connected),subscriptandsuperscript.--semantics-brand-ribbon-color, next to the ribbon widths: the color of the Rijksoverheid ribbon, whichnldd-top-navigation-barandnldd-page-footerboth draw. It does not follow a theme's accent color, since the ribbon does not either.Changed
--_button-background-colorinstead of--_background-color. A custom property inherits through shadow boundaries, and a generic name on one component reached the elements of another slotted inside it. Locals were never meant to be set from outside; if you did set one, it now has a different name.Breaking
nldd-step-indicatoris nownldd-step-bar, andnldd-timeline-track-cellis nownldd-step-cell. They are one family, the steps of a process across and under each other, asnldd-progress-barandnldd-progress-circleare one family for how much is done. "Timeline" said less than the cell does: most of all it draws steps. Rename the tags, and the classes and imports if you use them:nldd-step-indicator,NLDDStepIndicatornldd-step-bar,NLDDStepBarnldd-step-indicator-item,NLDDStepIndicatorItemnldd-step-bar-item,NLDDStepBarItemnldd-timeline-track-cell,NLDDTimelineTrackCellnldd-step-cell,NLDDStepCell@nldd/design-system/step-indicator@nldd/design-system/step-bar@nldd/design-system/timeline-track-cell@nldd/design-system/step-cellcomponents.step-indicator.*components.step-bar.*The attributes stay the same. An old tag is an unknown element now and renders nothing, without a warning, so search your markup.
The
--components-*custom properties no longer exist. If you set or read one, nothing happens now, without a warning. Most were never meant to be touched. The ones that more components share moved to semantics:--components-code-viewer-token-*-color--semantics-code-syntax-*-color--components-list-item-is-*,--components-list-item-indicator-*--semantics-list-items-is-*,--semantics-list-items-indicator-*--components-menu-bar-item-inline-padding--semantics-menu-bar-items-inline-padding--components-menu-box-shadow--semantics-overlays-box-shadow--components-menu-item-is-highlighted-background-color/-content-color--semantics-controls-is-highlighted-indicator-color/-contrast-colorSearch your own CSS for
--components-to find what this affects.Fixed
A transparent
nldd-toggle-buttonwithvariant="text"fills when it is on, also with aselected-icon. A transparent toggle with a selected icon stays without a fill and lets the change of icon show that it is on. Withvariant="text"the icon does not render, so nothing changed at all when you switched it on.nldd-toolbarno longer leaves an item in the overflow menu that fits. When the toolbar measured while the web font was still loading, an item could take its width from the fallback font, go into the menu, and stay there once the font arrived: a hidden item has no width to watch. The toolbar now measures again when the fonts have loaded. In the toolbar-with-actions pattern at 375px the tab bar went into the menu in 7 of 10 page loads.v0.8.95Compare Source
Highlights
The plugin is now
nldd-design-system, and it moved to the NLDD marketplace. Install it fromNederlandseDigitaleDienst/ai-pluginsasnldd-design-system@nldd. That marketplace is tool-independent, and the plugin now ships a Cursor manifest next to the Claude Code one. An existingnldd@nldd-pluginsinstall keeps working and keeps updating until 1 March 2027, when the marketplace in this repository goes away. The README has the four commands to move over.The current step in
nldd-step-indicatoris marked on the step itself, withstatus="current"on thenldd-step-indicator-item, instead of a number on the parent. Move the attribute when you upgrade. See Breaking.A title in
nldd-toolbarkeeps its width. Buttons move into the overflow menu first, and the title only truncates once none is left.Added
nldd-design-migratecovers sites built by a static site generator (MkDocs, Hugo, Eleventy). Leave the authors' Markdown alone and convert the rendered HTML in a build hook, take the bundle from the registry without a bundler and pin it with its integrity hash, and decide up front what is left without JavaScript and what loading the whole bundle on every page costs. The behavior check now also strips the base path a site runs under, because a pull request preview runs under a different one than production.Changed
A title in
nldd-toolbarkeeps its own width while items can still move into the overflow menu. It used to shrink first: next to a few buttons a title like "Aanvraag A-1042" came down to "Aanvraag A-…", or to nothing at all, while every button stayed. Now the items go first, inpriorityorder, and the title only truncates once none is left to move.min-widthonnldd-toolbar-titledefaults to the width of its text, up tomax-width. Setmin-width="0"for the old behavior.Only the ribbon and the wordmark text in
nldd-top-navigation-bartake a click. The logo link spans the whole width beside the ribbon, which keeps the ribbon centered, and all of that space used to be clickable. It is still one link with one tab stop, named by the wordmark.nldd-blockquotewraps withtext-wrap: pretty, the quote as well as the attribution, so neither ends on a single word on its last line.Breaking
The current step in
nldd-step-indicatorisstatus="current"on the step. It was a number on the parent (current="2"), which pointed at the wrong step as soon as an app left one out. Now it is the samestatusa step already had, with the same three values asnldd-timeline-track-cell:currentmarks where you are and the parent derives the rest from it, whilepastandfuturestay an exception for one step. With no step marked, step 1 is current, and with more than one the last wins. Move the attribute:<nldd-step-indicator current="2">becomesstatus="current"on the second item. A leftovercurrenton the parent no longer does anything, and a dev build says so in the console, also when a framework sets it later.The long form of a skill name is
/nldd-design-system:nldd-design, no longer/nldd:nldd-design, because the prefix follows the plugin name. This holds for existing installs too, from their next update. A permission rule such asSkill(nldd:nldd-design-build)or a hook that matches on the long form stops matching, without an error. The short form (/nldd-design) is unchanged, and so is the skill an assistant picks by itself. The README has a search that finds the places to change.Fixed
Running text no longer grows when a phone turns to landscape. iOS Safari enlarged the paragraphs in
nldd-rich-textwhile the titles kept their size.@nldd/design-system/stylesnow setstext-size-adjust: 100%onhtml, in the reset layer, instead of onnldd-rich-text. If you load only the tokens, set it on your own root.The stretching ribbon in
nldd-top-navigation-baris limited to Safari, and to every browser on iOS. Chrome does not draw anything above the top of the page, so on a pull it showed only the piece that holds still at the top, and a hard pull opened a gap between that piece and the logo. Chrome and the other non-WebKit browsers now show the plain gap above the ribbon again, as they did before 0.8.94.v0.8.94Compare Source
Highlights
The package speaks more languages. One call,
setTranslations(enUS)at startup, turns every component English, including the ones another component builds out of your reach, such as the activity indicator in a loading button and the calendar in a date field. US English ships complete, with concept sets in Frisian and in Papiamento as written on Curaçao and Bonaire and on Aruba. Docs/Vertalingen in Storybook explains the layers.nldd-text-editorchecks the spelling and handles typeaheads better. It checks the running text and leaves code, addresses and mentions alone. A typeahead query takes accents and a+, a list can decide what may follow its trigger, and an empty list says why instead of closing without a word.nldd-toggle-buttoncan be quiet. It takes four appearances fromnldd-button, the two transparent ones included, and withselected-icona transparent toggle stays without a fill when on: a "Volgen" toggle goes fromplustocheck-markand keeps its label.The ribbon in
nldd-top-navigation-barstretches when the page is pulled down, instead of opening a white gap above it.Menus line up, and use fewer icons. One checkbox or radio item gives the whole
nldd-menuits check-mark column, so the text of every group starts at one line. A new design guideline says when an icon in a menu adds something, and when it is only filler.Button bars and split buttons work on a colored surface.
nldd-button-barandnldd-split-buttontakeinherit-tintedandinherit-filled, like the buttons inside, instead of a gray block. The inherit buttons now keep one color through hover, press and expanded, so their label keeps the contrast the surface gives it.Attributes say what they do.
variantisappearanceon nine components where it was a designed style,levelonnldd-timeline-track-cell, andmodeisvariantonnldd-progress-barandnldd-progress-circle. The values stay the same; search your markup, since a leftover attribute is ignored without a warning. See Breaking.Added
Storybook opens on an introduction. Docs/Introductie says what the system is, how to install it and load the styles, where the components, patterns and design guidelines are, and how to add the Claude Code plugin. It used to open on Button, the first story in alphabetical order. Docs now sits at the top of the sidebar, with the introduction and the design guidelines first.
setTranslations()sets the texts for the whole package. Import it from@nldd/design-system/translationsand call it once at startup. A component now looks a text up in four layers: an attribute on the element, itstranslationsproperty, the package-wide layer, and the Dutch default. Calling it again re-renders the components on the page, so a language switch at runtime works too. In development it warns about a key no component knows, which is what an override does after a key is renamed.A complete US English set,
@nldd/design-system/translations/en-US, and the Dutch defaults as a template,@nldd/design-system/translations/nl. Spread the English set and add your own keys behind it to change a few texts:setTranslations({ ...enUS, 'components.activity-indicator.loading-label': 'Please wait' }).Concept translations in Frisian (
translations/fy) and Papiamento as written on Curaçao and Bonaire (translations/pap-CW) and on Aruba (translations/pap-AW). Every text is translated, also where the wording is a guess, since correcting a text is easier than writing one. No native speaker has checked them yet: report a better wording as an issue and they improve with each release.no-filteronnldd-combo-boxandnldd-token-fieldleaves the options as they are while typing, for a list your server already filtered per keystroke. Without it the field hides every option that does not contain the typed text, which also hides a server match on, say, an e-mail address. The part of an option that matches the typed text is still marked.A typeahead in
nldd-text-editorsays why its list is empty. It closed without a word, so whoever typed@could not tell "nobody matches" from "mentions do not work here". Where the list would be it now says "Typ om te zoeken" for an empty query and "Niets gevonden" for one without matches, both translatable.emptyTexton a list, andmentionEmptyTextfor the built-in@, are called with the query and can say more, such as "Kies eerst een kanaal bij Aan"; return null to close without a word, as before. The message is a status a screen reader announces, not an option you can select.A typeahead list in
nldd-text-editordecides what may follow its trigger.queryon a list, andmentionQueryfor the built-in@, take a regular expression for the text after the trigger. A list of people can allow one space, so@sam jansenfinds the right Sam. Lists on one trigger share the query of the first that sets one.inherit-tintedandinherit-filledonnldd-button-barandnldd-split-button, for a group of buttons on a colored surface. The buttons inside already had them, but the bar kept its gray surface behind them. Like a loose button, the group now takes its colors from the text color around it, andinherit-tinteddraws its see-through surface once for the whole group, so the tint does not stack.A design guideline on icons in menus. Icons are often used as filler, but are usually not needed and then do not help: add one only where it recognizably belongs to the item and helps people find or understand it, use the same icon for the same action everywhere, and leave it out when no icon makes the action clear at a glance. See Docs/Ontwerprichtlijnen under "Visueel en layout".
appearanceonnldd-toggle-buttonandnldd-toggle-button-group:neutral-tinted(the default),neutral-base,neutral-transparentandaccent-transparent. A toggle among transparent buttons, such as in a row of light actions under a message, stood out as a gray block. The transparent appearances have no surface at rest; when on, the toggle fills, so the state does not rest on a color alone.neutral-baseis for a tinted surface.nldd-toggle-button-groupforwardsappearanceto its buttons, as it doessize. New tokens:--semantics-buttons-neutral-transparent-is-selected-*and--semantics-buttons-accent-transparent-is-selected-*.selected-icononnldd-toggle-buttonshows another icon while the toggle is on, such asplustocheck-markon a "Volgen" toggle whose label stays the same. On the transparent appearances it replaces the fill: the toggle stays quiet when on, and the change of shape says it is on. On a surface, the surface fills as before.appearance="neutral-base"onnldd-segmented-control, for a segmented control on a tinted surface, asnldd-button-barhas it. The selected segment keeps its fill. For a quiet row without a track, usenldd-toggle-button-groupwithtype="radio"and a transparent appearance.The font files are exported as
@nldd/design-system/fonts/*. A site that serves the fonts itself without a bundler, such as a static site that only publishes its own folder, had to copy the woff2 files fromnode_modulesinto its own repository. A build step can now take them from the package, withimport.meta.resolve('@nldd/design-system/fonts/RijksSansWeb-Regular.woff2'), so the copies and their separate license notes can go. The terms inNOTICES.mddo not change: the path is only reachable.A card that is one link or button reacts to hover and press. With
hreforbutton,nldd-cardsteps its surface and its border one shade up on hover and one more while pressed, onbaseandtintedalike, as aneutral-basebutton does. A button lifted above the card in its footer does not set it off.loading-textonnldd-buttonandnldd-icon-buttonsays what the screen reader announces whileloadingis on, for a button that does more than load: "Aan het bewaren". Without it the indicator says its translated "Laden".The ribbon in
nldd-top-navigation-barstretches when the page is pulled down. Pulling a page past its top used to open a white gap above the ribbon; the ribbon now carries on into it and only grows longer. A bar above the navigation bar, such asnldd-status-bar, stays clear: the ribbon starts at the top of the page, not right above the logo. It works in browsers that support scroll-driven animations; elsewhere the gap stays as it was.Changed
nldd-text-editorchecks the spelling, like the other text fields, andno-spellcheckturns it off. It never did: CodeMirror switches spellchecking off. Now it is on for the running text and off for what the editor knows is not: inline code and code blocks, the address of a link and a bare URL (the link text is checked), and mentions. Autocorrect and autocapitalize follow the user's own settings, as in the other fields, also withno-spellcheck. Deleting a character or a selected word keeps the underlines on the rest of the line, as it does in a plain text field. Two limits come from the browser: text that was already there is checked once you edit near it, not when the editor opens, and markup that changes how a line is built, such as starting a list item, hides the underlines on that line until you type on. An editor that should not be checked takesno-spellcheck.The
inherit-tintedandinherit-filledbuttons keep one color through hover, press and expanded. The label on these buttons has only the contrast the colored surface around it gives, and every state color moved the background toward the label: on a surface that just met 4.5:1, a hovered or pressed button fell below it. In a button bar the states did not show at all, since the bar's own surface lay underneath. This applies tonldd-button,nldd-icon-button,nldd-button-barandnldd-split-button; the focus ring stays. The forty--semantics-buttons-inherit-*-is-*tokens for those states are gone, as nothing uses them any more.One checkbox or radio item in an
nldd-menugives every item in that menu the check-mark column. Only the checkable items had it, so a group of plain actions below a group of options started its text further left, and the menu could no longer be scanned from top to bottom. The text of the whole menu now starts at one line, as in a native menu. A plain item keeps the column empty and is announced as before. An icon gets no column of its own: it starts where the text of an item without an icon starts. A submenu decides for its own items.nldd-button-barwarns in development about annldd-toggle-buttoninside it. The bar is one surface for actions, and it showed a toggle button half: its own size and border, a selected fill under the bar's ring. Usenldd-toggle-button-groupfor a row of toggles, ornldd-segmented-controlfor one choice out of a few options.NOTICES.mdcovers the logo and the house style of the Rijksoverheid as well, not only the fonts. The logo innldd-top-navigation-bar, the favicon and the touch icon, the ribbon and the house style colors fall outside the EUPL: the Staat der Nederlanden reserved copyright on them, and they are for the Rijksoverheid and parties working on its behalf only. The coat of arms may only be used together with the ribbon. The README and thenldd-design-buildskill point to it.Running text wraps with
text-wrap: pretty, headings innldd-rich-textwithbalance.nldd-text, and the paragraphs, list items, definitions and quotes innldd-rich-text, no longer end on a single word on their last line, as the cells and the status components already did. Headings innldd-rich-texthadprettyand now break into lines of even length, the waynldd-titledoes. Browsers without support, Firefox for now, break lines as before.nldd-dropdownfades a long option out instead of ending it with an ellipsis. The field now shows its choice through the native<select>, and WebKit neither keeps that text out of the end padding nor draws an ellipsis there, so every browser fades the text before the icons. Two side effects: desktop Safari draws the options in its popup at the field's text size, 18px atmdinstead of 16px, and anxsdropdown shows 16px text on touch devices, because iOS zooms in on a smaller select when you tap it.Breaking
variantis nowappearanceonnldd-button,nldd-button-bar,nldd-icon-button,nldd-split-button,nldd-keyboard-shortcut,nldd-code-viewer,nldd-code-editor,nldd-text-editorandnldd-list. The values stay the same, sovariant="primary"becomesappearance="primary", and the property isappearancetoo.variantmeant two different things across the system: how something is drawn, and what kind of thing it is. On these nine it was always the first, a designed style with its color included, so it now has a name of its own.variantstays on the components where it says what kind of thing it is and sets the icon and role with it, such asnldd-banner,nldd-notification,nldd-status-barandnldd-inline-dialog. Avariantleft on one of the nine is ignored without a warning and the component falls back to its default style, so search your markup for it. The exported types follow:CodeViewerVariant,CodeEditorVariant,TextEditorVariant,ListVariantand theVariantofnldd-icon-buttonare nowCodeViewerAppearance,CodeEditorAppearance,TextEditorAppearance,ListAppearanceandAppearance.variantonnldd-timeline-track-cellis nowlevel.major,minorandnonesay how far down the hierarchy a row sits:minoris a row that belongs under the one above it. The values stay the same, sovariant="minor"becomeslevel="minor".modeonnldd-progress-barandnldd-progress-circleis nowvariant.progressanddistributionare two kinds of indicator, progress toward a goal and the parts of a whole, and the screen reader hears something different for each, which is whatvariantstands for in this system. The values stay the same, somode="distribution"becomesvariant="distribution". The typesProgressBarModeandProgressCircleModeare nowProgressBarVariantandProgressCircleVariant.Fixed
A typeahead query in
nldd-text-editortakes accents and a+.@józefstopped at theóand:+1:at the+, so the source never saw the rest. What may follow a trigger is now letters in any script and with any accent, digits,_,.,+and-, for every list. A space still ends the query, as it ends a mention in most systems.nldd-combo-boxwithallow-customkeeps a value chosen from the menu. Leaving the field, or pressing Enter, committed the shown text as the value whenever it differed from the value. That is the normal state after every choice ("Nederland" fornl), so in a form the label went to the server instead of the value, usually on the click on the submit button. Only text the user typed is committed now, and typing an option's label in full picks that option.nldd-token-fielddid the same with a typed label: "België" became a token with the value "België" instead ofbe, on Enter, on a comma or when leaving the field. It now adds the option.nldd-combo-boxandnldd-token-fieldopen when the options arrive after the keystroke. A field that queries its server per keystroke slots the matches in a moment later. Withallow-custom, and on the token field always, the keystroke found nothing to show and kept the menu shut, and nothing looked again when the options came in, so the list only appeared on the next letter. The options that arrive now open it, as long as the user is still in the field and did not press Escape. A menu whose options all go away closes again.A component that another component builds can be translated. The activity indicator in a loading
nldd-button,nldd-icon-buttonandnldd-inline-dialog, the tokens ofnldd-token-field, the overflow menus ofnldd-toolbar,nldd-menu-barandnldd-document-tab-bar, and the pickers ofnldd-date-fieldandnldd-time-fieldsit in a shadow root, so thetranslationsyou set on the outer element never reached them and they stayed Dutch. They readsetTranslations()now.nldd-dropdownshows the value your app sets. A value set withselect.value,selectedIndexoroption.selectedkept the old label on screen until someone picked an option by hand, so a form that loaded saved data showed a different choice than it held. The label was a copy in the shadow DOM that only updated onslotchangeandchange. It is gone: the<select>now shows its own choice, so there is nothing left to go stale.The changelog no longer repeats its introduction under 0.8.93.
The
globalandutilityslots ofnldd-top-navigation-barare documented. The component has always rendered both, each taking annldd-menu-bar, but neither had an@slotline.custom-elements.jsonand the reference innldd-designtherefore listed no slots for it, and a check that validates markup against the manifest rejected<nldd-menu-bar slot="global">. The default slot ofnldd-progress-circle, where its segment indicators go, was missing the same way.v0.8.93Compare Source
Highlights
The patterns are live in Storybook, with their examples running and a test on what each one promises. The pages in the skill are generated from that same markup, so what you copy is what you saw.
The skills are split and renamed, so the name says the task.
nldd-designholds the reference, the changelog and the design guidelines, andnldd-design-build,nldd-design-migrate,nldd-design-upgradeandnldd-design-contributeeach cover one job. A skill namednlddstays behind to catch the old name until 1 March 2027. Update the references in your own instruction files first: a skill that is not found warns nobody, it just makes the answer worse.Four design guidelines come with this release, on where work that needs its context belongs, on a filtered list as a mode, on sticky headers, and on the two shapes a heading can take. They are in
nldd-design, and Storybook has them under Docs.Breaking
The page sections no longer take
scheme.nldd-simple-section,nldd-full-bleed-section,nldd-hero,nldd-sidebar-sectionand the two- and three-column sections could pinlight,darkorinvertedon a band, which made one section decide the color scheme for everything inside it. A section still draws its surface withbackground, which is what the components inside read; light and dark stay a choice for the page, throughcolor-schemeon a root element.nldd-windowkeeps its ownscheme.nldd-button-groupfollows its own width, and that is its default.orientationwashorizontalorvertical, withverticalas the default, so every group that did not say otherwise stacked its buttons at every width. It now takesautoas well, and that is the new default: a row, and stacked over the full width once the group itself is narrower than the sm breakpoint. The group is its own container query, so the same group is a row in a page and a stack in a sheet beside it, without a media query of yours. A group that wants to keep stacking everywhere saysorientation="vertical". A stacked group stretches the buttons through--context-button-width, whichnldd-buttonnow reads, so a button follows the parent that stacks it without anyone settingwidth="full". The group used to write that attribute onto its children and take it off again; it no longer touches them, so CSS or a test of yours that keys on[width="full"]inside a group stops matching.The skills are now
nldd-design,nldd-design-build,nldd-design-migrate,nldd-design-upgradeandnldd-design-contribute.nlddmeant four things at once: the organization, the npm package, the plugin and the skill inside it, so the name said nothing about what the skill was for and its description had to carry an anti-trigger instead. The names now say the task. What is shared between them, the component reference, the changelog and the design guidelines, moves tonldd-design: that is reference material whether you are building something new or converting something old, and a migration needs it as much as a new app does.Raising the version of an app that already runs on this system is its own skill,
nldd-design-upgrade. It sat as a section inside the build skill, which is the wrong place twice over: an upgrade is not building something new, and it is not a migration either, so whichever of the two you reached for, the guidance was in the other one. It matters here more than in most systems because this project releases everything as a patch, including a breaking change, so the version number tells you nothing about whether a jump is safe. That is a deliberate choice in.releaserc.json, not how semantic-release behaves by default, which makes it easy to assume the usual semver signal is there when it is not.A skill named
nlddstays behind to catch the old name, and does nothing but point at the ones that replaced it. It is deliberately findable by the model and not just by/nldd, because the reference that breaks most quietly is a line in your ownCLAUDE.mdsaying "use the nldd skill": nobody types anything there, so a slash command would not catch it. It is removed after 1 March 2027, so update your references rather than lean on it.The plugin is still called
nldd. Installing it does not change, andenabledPluginsandextraKnownMarketplacespoint atnldd@nldd-plugins, the plugin, so leave those alone.What to check, quietest first, because the first two give you no error at all:
CLAUDE.md,.claude/rules/*.md,AGENTS.md, or the body of a skill of your own that says "use the nldd skill". Claude reads the instruction, does not find the skill, and carries on without it. Nothing warns; the output is just worse..claude/agents/*.md) and in hooks that match on it. Equally silent.Skill(nldd)in permission rules. It stops matching, so you get a permission prompt where you had none.claude -p "/nldd ...". Since Claude Code 2.1.273 an unknown slash command is no longer a free deterministic rejection but a model call that answers with a "did you mean" text, so a script checking the exit code still sees 0 and now gets billed, variable output instead of a fixed error./nlddinteractively. The loudest and least harmful.Invoke a skill either way:
/nldd-design-buildis the short form, and/nldd:nldd-design-buildalways resolves to ours even when a skill of your own carries the same name.The
subtitleslot ofnldd-titleis nowsupporting-text. That is what the text under a title is called in every other component, includingnldd-top-title-bar. Renameslot="subtitle"toslot="supporting-text", or use the newsupporting-textattribute for plain text. Content still inslot="subtitle"is not shown, and in development the console says once which rename it needs.nldd-form-sectioncalls its supporting textsupporting-textthroughout. The span in the legend is now.form-section__supporting-textinstead of.form-section__subtitle, and its token--semantics-forms-section-supporting-text-fontinstead of--semantics-forms-section-subtitle-font. The attribute already had that name. Rename the class in your CSS and the token where you override it: the old ones do nothing any more, without a warning.nldd-pagein a split-view pane no longer carries the document's landmarks. A page carries them: its header is the banner, its content the main landmark, its footer the contentinfo. A document has one of each, so two pages beside each other were two of each, which is invalid HTML that nothing on screen gives away. A page in a pane of a navigation, side-by-side or stacked split view, or inside an overlay, now renders a section and a plain div instead, which drops all three at once: a header inside sectioning content is no longer a banner.Set
landmarks="page"on the page in the pane that holds your primary content, because only the application knows which pane that is. Nothing warns when you do not: the app shell simply has no main landmark, and a screen reader user loses the jump to the content. Give the other panes anaccessible-labeland they are named regions instead, which is a landmark worth having.landmarksalso takesregionto step down anywhere else, and a second page that still renders a main says so in development.nldd-icontakesiconinstead ofname. Every other component that renders an icon already called iticon, so you had to remember the one exception. Renamename="…"toicon="…"on everynldd-icon. An icon that still hasnamedraws the dashed placeholder circle, and in development the console says once which rename it needs.The
--components-title-*tokens are gone.--components-title-{sm,md,lg}-overline-fontand--components-title-{sm,md,lg}-subtitle-fontstood invariables.css, butnldd-titlenever read them, so overriding one changed nothing. The overline and the supporting text take--primitives-font-body-sm-regular-tightand--primitives-font-body-md-regular-tight. The two names are removed rather than renamed, so avar()of your own that points at one resolves to nothing.Added
nldd-list-item-segmentcan be the button that opens a menu. It takespopup-type,popovertargetand the popover-invoker properties the buttons already had, so a row can carry a "more" action that looks like part of the row instead of a button dropped on top of it. Annldd-menuanchored to the segment seeds its own type and keepsaria-expandedin step, the same as withnldd-icon-button.nldd-buttontakes aformattribute. A save button in a sticky footer sits outside the form it saves, and pointing it at that form did not work: the platform resolvesformto a real<form>element, whilenldd-formrenders its own form without handing it the id you wrote. So the button found an element that was not a form, ended up with no form owner, and did nothing at all when clicked. It now resolves the id itself and takes the form annldd-formrenders, as well as a plain<form>. An id that finds nothing, or annldd-formthat has not rendered its form yet, says so in development instead of letting the click do nothing.A skill for migrating an existing codebase (
nldd-design-migrate). What goes wrong when you convert an application that already exists, and how you notice before your users do. The system fails silently, so the skill is built around checks that make the silence audible: what to verify before you convert anything, how to measure what a page still does rather than how it looks, and what to do when the fault is ours rather than yours. Separate files cover coming from Tailwind and rendering HTML on the server with fragment swaps (htmx, Turbo, Unpoly, LiveView), where one rule explains most of the trouble: a swap does not run a component's lifecycle the way a page load does.A skill for raising the version (
nldd-design-upgrade). Reading the changelog as an upgrade path: which entries to read first, what to search your own code for, and why renamed CSS variables are the trap that catches most people. Your own theme overrides then point at a name that no longer exists, with no error and no visible break, just a silent fall back to the default.A skill for proposing a change (
nldd-design-contribute). Where a report goes first, which depends on who you are: inside NLDD it goes to the design system channel on Mattermost, which is shorter than an issue and where the maintainer reads along, and from outside NLDD it goes to a GitHub issue. Then how to write it so it gets somewhere: describe the problem in your interface rather than only the component you have in mind, because often something existing already does it, and when it does not, your context shapes the design. It also states plainly what to expect, that the maintainer decides and a rejection comes with a reason, and it carries the rule that keeps the pattern set small: a composition used by several teams is a pattern of the system, one that lives in a single app is that app's habit for now.CONTRIBUTING.mdstays the source for maintainers; this is the consumer's half of it.The patterns are live in Storybook. Every pattern of
nldd-design-buildhas a page under "Patronen", with its example running and a test on what the pattern promises: that the sheet closes once, that a confirmation puts the way out first, that a page has oneh1. The pattern pages in the skill are generated from the same markup, so the example you copy from the skill is the one that is tested. Rules about a single component moved out of the patterns and into that component's documentation, and a design rule is stated once, in the design guidelines. The content page came along as a pattern of its own: it used to travel as a loose example, without a running example and without a test.Four design guidelines, and a section that says what it holds. Work that needs its context belongs in a sheet, not a modal or a page of its own, and a sheet buys you the full height of the screen. A filtered list is a mode as soon as you cannot see which filters are on. Avoid a sticky header: the content matters more than the bar above it, with the title bar that says what you are looking at as the one exception. And a heading is either a short summary written as a sentence or a label kept as short as it goes, which is a choice about what the reader is doing rather than about the kind of page. "Microcopy en toon" is now "Copywriting", because it covers headings as well as the small print.
openonnldd-sheet,nldd-modal-dialog,nldd-windowandnldd-popover. Bind it instead of callingshow()andhide()from your own code, for example<nldd-sheet :open="isOpen" @close="isOpen = false">in Vue. The overlay clears it when it closes another way, by Escape, the backdrop or its close button.show()andhide()keep working and set it too. Onnldd-popoverit replaces the read-onlyopenproperty, and opening still needs an anchor. The build skill, its Vue example and the patterns now bindopeninstead of callingshow()andhide()from a watcher, and the migration skill tells a server-rendered app to putopenin the markup, which needs no JavaScript at all.text,supporting-text,overlineandheading-levelonnldd-title.sizesets how big the title looks,heading-levelwhich heading it is, and you now see both side by side on the element. Withoutheading-levelthe text is a paragraph, for a title that is not a heading. Each text keeps a slot for content that is more than text, such as a link: the default slot,overlineandsupporting-text. A filled slot takes the place of its attribute.heading-levelonnldd-top-title-bar. The title was always anh1. It still is by default, and you can lower it where the bar is not the top of the page.Changed
A stacked
nldd-button-groupleaves an icon button at its own size. Everything in a stack fills the width, which turned annldd-icon-buttoninto a full-width bar with one glyph in the middle: wider, not a bigger target. Onlyautois a container query now, so onlyautohas to take its width from its parent: a group pinned tohorizontalorverticalsizes itself again, which it could not do in a parent that shrink-wraps (an inline-flex box, a float, a table cell, a flex item atwidth: auto). Anautogroup that ends up in such a parent measures zero and says so in development.The edge of a surface is the same grey as a line inside it again.
--semantics-surfaces-base-border-colorand--semantics-surfaces-tinted-border-colorgo back toneutral-75in light mode andneutral-200in dark, the values--semantics-dividers-colorcarries, which undoes the lighter edge of 0.8.89. Everything that draws its outline from these tokens is a step more present:nldd-table,nldd-list,nldd-code-viewer,nldd-rich-textand the boxes that follow them. Override the two tokens to keep the lighter edge.nldd-sheetandnldd-windowtake their name from the title bar. Withoutaccessible-label, a screen reader called them "Venster" unless you repeated the title there. They now take thetextof thenldd-top-title-barinside them, and follow it when it changes.accessible-labelstill wins, for a name that has to differ from the title. The development warning only appears when neither is there.nldd-list-itemwarns about anything in a row that is not a cell or a segment. Bare text or a loose element gets none of the typography, size and alignment a cell brings, and in a clickable row bare text falls back to the browser's button font. The row now says so once in development. Wrap the content in a cell.nldd-list-itemwarns about a control inside a row that is its own control. An icon button or a segment in a row withhref,button,checkboxorradionests a control inside a control: invalid HTML, and a second tab stop. The documentation already said the row warned about this, but it did not. Adecorativecheckbox or radio button, which only shows the row's state, is fine.nldd-toolbar-itemwarns when it has nothing inslot="overflow". On a narrow toolbar such an item moves into the overflow menu and its action is gone, which you never see on a wide screen. The item now says so once at load in development. A tab bar is no exception: its alternative is a menu group oftype="radio"items.nldd-rich-textlooks through a single wrapperdiv. The rhythm and the width zones apply to the direct children, so content wrapped in onedivbecame a single block and its paragraphs sat against each other. A markdown renderer or the root of a framework component produces exactly that wrapper, and you cannot always remove it. A lonedivwithoutclass,style,roleordata-widthis now passed through, and its children are laid out as if they were direct. A wrapper with any of those stays one block, since you gave it something of its own.The element that holds the notifications is
nldd-notifications-area. It wasnldd-notification-region, a singular name for the thing that owns every notification at once, and "region" named its ARIA role rather than its job. The id is the only part of it you can see, in the DOM; nothing about the notifications themselves changed. The three overlays that take it in,nldd-sheet,nldd-windowandnldd-modal-dialog, now document theirnotificationsslot, which the package fills itself.The CSS variables are documented as internal. They are the wiring of the components, not a public API: they get renamed, merged and removed whenever a component asks for it, and the changelog describes such a change from that component, not from your stylesheet. Steer a component through its attributes. Reach for a variable only for something that is not a component at all, and then at your own risk.
nldd-tokennames its buttons after itself. The dismiss button was called "Verwijder" on every token, so a row of filters read as a row of identical buttons unless you composed adismiss-textper token. Unset, it is nowVerwijder "{text}", and the menu buttonToon opties voor "{text}", with the token's own text in the quotes. Adismiss-textormenu-textyou set still replaces the whole label. Both properties now default to an empty string instead of the bare word. The words themselves come fromtranslations, like every other user-facing string in the package:components.token.dismiss-action,components.token.open-menu-actionandcomponents.token.open-token-menu-action.nldd-token-fieldnames its tokens the same way, through its own translations, wherecomponents.token-field.token-menu-actionreads "Toon acties voor" so the token's text can follow it.Fixed
nldd-windowopens whenshow()comes before its first render. Sheet and modal already waited for their dialog. The window did nothing and said nothing, so a window opened in the same tick it was created stayed shut.The validation icon of
nldd-token-fieldstays in the top corner. It was centered over the whole field, so the moment the tokens wrapped to a second row it drifted down the side, away from the row it belongs to. It now sits wherenldd-multi-line-text-fieldputs its own: at the top, level with the first row.A tree row's divider no longer jumps when the branch opens. The chevron turned by rotating the whole
nldd-icon-cell, and a rotated element reports a turned box togetBoundingClientRect(). That box is what the row measures to place its divider, so the same row drew its line 12px further left while it was expanded. The cell now turns its glyph instead, through theiconpart it exposes, and keeps its own box where it was.nldd-list-itemdocuments where a row's divider starts.divider-startanddivider-endgo on a cell in the row and move the line off its default, the first text or title cell. The item has always read them, but only its prose said so, so neither the reference nor the markup validator knew they existed. An indented tree row could not get its line to start at the icon without guessing.nldd-bar-split-viewdocuments the order attributes of its panels. The component readssm-order,md-orderandlg-orderoff its children to place a bar above or below the content per breakpoint, but only its prose said so, so the reference listed neither the attributes nor what they do. A bottom bar on a phone was there all along and nobody could find it.nldd-validation-itemhas a description in the reference. The component reference takes the prose above the first@elementin a file. Two components that share a file write their own prose under their own@element, and that was thrown away, so the item shipped as a bare table of attributes and the rule about giving it an id that names its field reached nobody.nldd-navigation-split-viewlost a line the same way. Both are back, andnldd-formnow says when it marks a field as invalid.hiddennow hides every component. Nine components stayed visible withhiddenset, because their owndisplaybeat the browser's rule for the attribute.nldd-list,nldd-sheet,nldd-navigation-split-view,nldd-menu,nldd-progress-bar-segment-indicatorandnldd-formnever hid.nldd-number-fieldwithwidth="full",nldd-paginationwithcenteredand annldd-activity-indicatoraround content did not hide either.Configuration
📅 Schedule: (in timezone Europe/Amsterdam)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.
77669bb57b590eec670aUpdate dependency @nldd/design-system to ^0.8.93to Update dependency @nldd/design-system to ^0.8.94590eec670ab696687bdeUpdate dependency @nldd/design-system to ^0.8.94to Update dependency @nldd/design-system to ^0.8.95b696687bde62fd465152Update dependency @nldd/design-system to ^0.8.95to Update dependency @nldd/design-system to ^0.8.9662fd465152f3ef8d8992Update dependency @nldd/design-system to ^0.8.96to Update dependency @nldd/design-system to ^0.8.98f3ef8d89922e799e2234Update dependency @nldd/design-system to ^0.8.98to Update dependency @nldd/design-system to ^0.8.992e799e223437b0eb6e11Update dependency @nldd/design-system to ^0.8.99to Update dependency @nldd/design-system to ^0.8.10037b0eb6e11c996e289f4Update dependency @nldd/design-system to ^0.8.100to Update dependency @nldd/design-system to ^0.9.0c996e289f47012667e86Update dependency @nldd/design-system to ^0.9.0to Update dependency @nldd/design-system to ^0.10.0View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.