Update dependency @nldd/design-system to ^0.8.86 #341
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.82→^0.8.86Release Notes
MinBZK/storybook (@nldd/design-system)
v0.8.86Compare Source
Highlights
No
'unsafe-inline'in yourstyle-srcfor our components. Two of them wrote styling into the page itself, which is exactly what a Content-Security-Policy stops: under a strict policy a cell hid at no width at all, and a progress circle lost the color of its ring, both without a word. The rules travel through the CSSOM now, which a policy leaves alone. What each one did is in Fixed below.A favicon, as
@nldd/design-system/favicon.svg. The package shipped the logo, the fonts, the reset and the tokens, and left the tab strip to every site to redraw. They drifted, down to two shades of the same blue. This is the arms filling the tile on the lintje#154273, the pattern logius.nl and MijnOverheid Zakelijk already use, and the one that survives 16 pixels where the ribbon turns to mush. Copy it into whatever you serve statically and point a<link rel="icon">at it. A browser loads a favicon apart from the page, so your CSS cannot reach it: another background is a copy of the file with onefillchanged, which is what the sites that put their own house color around it are doing anyway. There is a PNG of the same tile beside it,@nldd/design-system/touch-icon.png, because Safari will not take an SVG for the icon on the home screen. It is 180 square, the size Apple asks for, and it is one file rather than a ladder of sizes. This Storybook now wears both.On a narrow screen the wordmark beside the ribbon is centred against it. It started at the ribbon's midpoint and grew down from there, so a one-line wordmark hung against the bottom half of the mark with the space above it empty. It is centred now, and only what does not fit grows downward: centring a taller wordmark would push its first line off the top of the page, which is where the ribbon starts. Once it does grow it keeps 12px from that edge, and nothing under the last line, so the bar is no taller than the text in it. An auto margin does the work, so there is no height to guess and no breakpoint to keep in step with the text. Above sm nothing changes.
Breaking
A CSS length in
gaponnldd-collectionno longer applies. It was the only component that took one. Use the step with the same number:gap="16px"becomesgap="16". A gap that has to change with the viewport issm-gap/md-gap/lg-gaprather than a length.--context-layer-topand--context-layer-bottomare now--context-inset-topand--context-inset-bottom. The value says how much of the edge is taken by chrome that a sticky element has to clear, and layer reads as stacking order, which is whatz-indexand@layerare for. Inset is the word the platform already uses for this, as inenv(safe-area-inset-top)and Android's window insets, and the same number is whatscroll-padding-topwants, which has nothing sticky about it. Rename it wherever your app publishes or reads it. A CSS variable that no longer exists fails without a word, so nothing warns: sticky content lands against the top edge instead of below your bar.nldd-app-view,nldd-page,nldd-sheet,nldd-bar-split-viewandnldd-modal-dialogall speak the new name.Added
gapis a step of the spacing scale in both components, and so ispaddinginnldd-container.nldd-collectionreadgapas a CSS length andnldd-containerread it as a scale step, sogap="16"was right in one and wrote--_gap: 16in the other, which is not a length: the declaration fell away and the space between the items became nothing at all, without a word. One meaning now, behind one resolver. Spacing is rhythm, so a value beside the scale is not a finer choice but a break in the pattern, and a length is refused like any other value that cannot be placed: nothing is written, the default stands, and dev names the value rather than leaving you to spot the collapse.nldd-collectiongainssm-gap,md-gapandlg-gapon the way, whichnldd-containeralready had and which is where a gap that has to vary belongs.layout="lanes"onnldd-collection. Items of unequal height packed into columns, the masonrynldd-containerhas had for a while, now on the component that pages.item-widthis the minimum column width, the same as it is ingrid. Where the browser hasgrid-lanesit uses it; where it does not it falls back to that grid rather than to the multicolnldd-containerfalls back to. That is the whole reason the two differ: multicol fills column by column and redistributes every item each timeshow-load-moreadds to the set, so what you had already read moves. Grid fills row by row, like native lanes, and grows at the bottom. What you give up in the fallback is the ragged edge, which is a look rather than a behaviour.A row can be a radio:
radioonnldd-list-item,type="radiogroup"onnldd-list. A choice out of a handful used to mean annldd-radio-button-group, which gives every option a label and nothing else. As rows the options can carry what a filter list wants beside them, a count that lines up on the right and reads as secondary, and the whole row is the target instead of the dot. The row is the control, the waycheckboxalready was: the action becomes arole="radio"button witharia-checked, activation setscheckedand never clears it, andchangefires once, the way a native radio stays put when you pick what was already picked. Slot annldd-radio-button decorativefor the shape. The group is the parent:type="radiogroup"makes the items region arole="radiogroup"and the rows step out of the tree withrole="none", so the radios are the group's own children rather than list items wrapped around them.Changed
nldd-page's sticky header and above its sticky footer was 32px, and the default sticky inset ofnldd-sidebar-sectionwas 16px, so a box that cleared the header still began inside its fade. Both are 24px now, which is what lets the sidebar start where the fade ends. Nothing to set for it, andsticky-top/sticky-bottomstill override.Fixed
An empty list and a list that found nothing are two states now, and
[slot="no-results"]is the second one. They used to be one:[slot="empty"]said the same sentence to someone who has no assets yet and to someone whose search matched none of their twenty. The list can tell them apart, because it knows both how many rows it has and how many are[hidden], so it now picks the one that fits and falls back toemptywhenno-resultsis not given.What changes with it is the chrome. Rows that are only filtered away keep the search field and the
[slot="toolbar"], because those are the way back to the rows: since 0.8.84 a query that matched nothing took the whole list off the page, search field and filters included, leaving no way back at all. No rows at all hides them instead, since there is nothing to search or filter, and with nothing in[slot="empty"]the list still leaves the page entirely. A list that fetches its rows is in that state until they arrive: put annldd-inline-dialog variant="loading"inemptyto hold the place, rather than have the controls appear a moment later. And a slot with nothing in it draws no box any more, where an empty surface read as a skeleton that never loaded.A long word in the wordmark no longer shoves the ribbon off centre. A grid item is at least as wide as its longest word unless it is told otherwise, and the wordmark sits in one of the two tracks that hold the ribbon in the middle. One unbreakable name pushed the ribbon 137px to the left and the page 43px past the screen, so a phone got a horizontal scrollbar on the bar alone. The name wraps mid-word now, with
overflow-wrap: anywhererather thanbreak-word, because only that one counts the break when the track is measured. Wrapped and not truncated: a name cut off by an ellipsis cannot be read at all.The wordmark measures itself against the ribbon of the breakpoint it is in.
--_logo-widthwas declared per breakpoint inside the logo element itself, so everything outside it kept reading the small value: on lg the wordmark reserved 80px beside a 96px ribbon and the spacer that places it was 8px short. The declaration sits on the bar now, where the ribbon and the wordmark both read it.Sticky content inside a page clears the page's own bars.
nldd-pagesaid nothing about its sticky header, so annldd-sidebar-sectionin one stuck to the top of the viewport and slid under it: the top of the sidebar sat behind the bar, and the height cap was a whole viewport where a viewport minus the bars was left, which ran the bottom of the box off the screen. The page now adds its sticky header and footer to--context-inset-topand--context-inset-bottomfor its content, the same variables the shell already uses to tell the page where it may stick.nldd-sidebar-sectionreads them, so its box lands 24px below whatever is above it and ends 24px above whatever is below, the depth of the fade a sticky header casts, so it starts where that fade ends, andsticky-top/sticky-bottomgo back to being for chrome the page cannot know about. The heights are measured rather than assumed, because a top title bar shrinks as you scroll past its anchor. While the page owns the scroller the bars outside it do not count, since a sticky offset there is measured against the scroller rather than the viewport. The page publishes what it is scrolling too, as--context-scroller-height, and the sidebar caps its height on that instead of on100dvh: with the page as the scroller a viewport-tall cap hung out the bottom by exactly the height of the chrome around it.A named
hide-beloworhide-aboveno longer costs you'unsafe-inline'in yourstyle-src. The@containerrule arrived as a<style>element written into the shadow root, and a Content-Security-Policy counts that as an inline stylesheet wherever it sits. On a strictstyle-src 'self'the browser dropped it without a word and the cell showed at every width, so a timeline drew its mobile and its desktop variant on top of each other. The four named breakpoints are static CSS now; nothing changes in how you write them. A custom length (hide-below="320px") is the one threshold that cannot be static, so its rule is still written while the element runs. It goes into a stylesheet the shadow root adopts rather than a<style>element, and a policy judges the element rather than the CSSOM, so that path needs no'unsafe-inline'either. No component writes a<style>element into its own shadow root any more.nldd-progress-circlekeeps its ring color under a strictstyle-src. The border on the track and on each segment is an SVG filter that floods the edge with a color, and that color went in as a literalstyle="flood-color: …". A policy judges an inline style attribute the same way it judges an inline stylesheet, so onstyle-src 'self'the browser dropped it and the filter fell back to its default, which is black. The two fixed colors are ordinary rules in the component's stylesheet now, and the one that varies per segment is set through the CSSOM, which a policy leaves alone.v0.8.85Compare Source
Highlights
boxonnldd-icon, for an icon on a filled square. A feature card with a tinted tile, a menu with a colored category icon: common enough that consumers build it themselves out of a box and an icon, and then pick the two colors by eye. This turns the color question around instead.colorandcustom-colorpaint the box, and the glyph takes whatever contrasts with it, white or black, chosen on luminance by the same token the badge uses. That pair is the reason this is an option on the icon rather than a composition: it is the part nobody can check by looking.sizethen measures the box, so the same size renders a smaller glyph withboxthan without: four fifths of it, with a corner radius of a fifth. Both are ratios, so a decision about how a glyph sits in its box is one number in one place rather than a shape every consumer redraws.A tulip, in two weights.
tulipdraws the flower as an outline: three petals, the outer two curling up beside a pointed middle one, over a short stem.tulip-lightis the same flower with a thinner line, drawn on a 32 view-box like the other light weights, so its line stays 2 pixels at the size it is meant for. In this set-lightnames a weight rather than a state, the waycircle-lightreads besidecircle.Added
tulipandtulip-light. No aliases: the name is the flower, and it says what the icon draws.Changed
A line of
nldd-textstops at 40em. It had no maximum at all, so in a wide column one sentence ran the full width and became a ruler. 40em is the measure the rest of the system already holds:nldd-rich-textcaps its main column at 720px and a blockquote at the same, both against a body-md of 18. In ems rather than pixels, so the line holds the same number of characters at every size and a size added later is covered without anyone remembering to. New token,--semantics-text-max-width; override it, ormax-widthon the element, where a line is meant to run the full width, such as a label in a table cell.viewfinderhas the corners ofviewfinder-line. Each of its four arms is a pixel longer, so a corner now reaches 6 pixels out instead of 5. The two are one pair,fit-to-viewandscan, andviewfinder-linealready had the longer arms. Side by side the shorter ones read as a smaller icon rather than the same frame with a line through it.Fixed
nldd-buttongets the size the button gives its own. The button measures the icon it renders fromstart-iconin a span of--_icon-size, but anything you slot intostart-iconorend-iconlanded in a bare slot and kept whatever size it arrived with. That slot is where a mark that is not one of our icons goes, a third-party logo for instance, and every consumer had to size it by hand against a token they first had to find. The size now applies to the slotted element itself rather than to a wrapper around the slot, so a button with no icon at all still reserves no room for one.v0.8.84Compare Source
Highlights
nldd-validation-list, everything a value has to satisfy in one list. A requirement you can state up front is an item with a rule (minlength,maxlength,matchorrequired) and it checks itself while you type. One only a server can decide is an item without a rule, and the app names it inunmeton the control.judgingswitches the two modes: before a verdict the list states what the field wants, after one those hints are gone for good and what is left is whatever the value fails. What turns red isinvalidon the control and nothing else, so a line never goes critical under a field that looks fine. Hints are off by default: a phone number does not need its format spelled out before anyone has typed, and a password does.A timeline row can leave out its dot and stay part of the track.
variant="none"keeps the lane the row stands in and the status of the track running through it, so a group of rows under one step reads as one stretch instead of breaking the line. Each attribute answers one question now:sizeis the lane,variantwhat stands in it,statushow far along you are. On the row that closes a track,position="only"leaves the line out altogether. See Breaking for the mapping.The track lines carry the same ring as the dot, on their sides only. The dot already read over the line running under it while the line ran straight into whatever sat beside it. A spread would have banded the top and bottom too and cut the track at every row, so two offset copies of the line paint the sides and nothing else.
Two icons.
notefor what you scribble beside the work, where thefile-textfamily is a document you file.screwdriver-wrench, aliased astools, for the work itself rather than for a setting, which is whatgearis and stays.Breaking
error-messageon an input is nowunmet, and there is no fallback. It names the ids of the items a value does not satisfy, which is what annldd-validation-listreads. Consumers replace the attribute wherever they set it, bound forms included: Angular's[attr.error-message]becomes[attr.unmet]. Nothing warns.nldd-form-fieldreads an attribute that is not there, so every message on the page silently stops appearing. One search and replace onerror-message, minding thaterror-message-idsis a different, internal thing and keeps its name.nldd-form-field-error-textis gone. An error you have to fix is a requirement the value does not meet, so it belongs in annldd-validation-listbeside the requirements that state themselves. Migration is per field and the ids travel with the texts:<nldd-form-field-error-text id="password-length">Must be at least 8 characters.</nldd-form-field-error-text>becomes<nldd-validation-item id="password-length" minlength="8">At least 8 characters</nldd-validation-item>, and anything only a server can decide becomes an item without a rule, still named inunmet. Leaving the old element in place is worse than a blank: an undefined custom element has no shadow styles, so every message renders permanently. Search for the tag; nothing warns.Validation texts are the requirement, not the instruction. The same line does two jobs, a requirement up front and, once the field is judged, what is still wrong. So "Use at least 8 characters" becomes "At least 8 characters", "Pick at least one option" becomes "At least one option", and "Enter a valid company number (8 digits)" becomes "A company number of 8 digits". This touches every message a consumer has written; the examples are in
skills/nldd/SKILL.md.An empty
nldd-listornldd-tablesays nothing of its own.empty-textandempty-supporting-textare gone from both, and with them the defaultnldd-inline-dialogthey fed. What an empty list means is the app's to write: "no assets yet" and "nothing matched that search" are different sentences with different next moves. Measured in the two applications that lean on this system hardest: 324 of their 330 lists set neither the attributes nor the slot, so all of them fell back on the same Dutch "Geen items" in an English interface, while 62 inline dialogs stood beside those lists in the consumer's own markup because the slot was never found. Put annldd-inline-dialogin[slot="empty"], which already worked and is now the only way. Empty with that slot unfilled, both take themselves off the page rather than drawing an empty surface: on a boxed list that was a tinted bar with nothing in it, which reads as a skeleton that never loaded, and it made "nothing" look different per variant. They warn once in development instead, so a blank area becomes a question to whoever is building rather than a silence for whoever is reading.nldd-menukeeps its default: a filter that matches nothing is a state of the control, not content of the app.nldd-timeline-track-cellsplitsvariant,minorandstatusintosize,variantandstatus.variant="dot|step"set the dot and the lane at once,minorsat beside it, andstatus="none"took the dot away from a list of values that is otherwise about progress, so a row without a dot lost the lane it had to stand in. Mapvariant="step"tosize="md",minortovariant="minor", andstatus="none"tovariant="none"plus the status that fits.sizeissm(16px) ormd(24px), and a number fits inmd.selectedonnldd-tab-bar-itemis nowcurrent. The name every other navigation component uses:nldd-menu-bar-item,nldd-breadcrumbsand, since 0.8.83,nldd-list-item. A bar that switches content still rendersaria-selectedand anavigationbararia-current="page", and it was that second mode where the old name was wrong. Rename one attribute per bar. The old one does nothing, and warns in dev.nldd-document-tab-bar-itemkeepsselected, because those are tabs over documents rather than routes.Added
A semi-bold row in the body type scale. The weight primitive was already there, 550, between medium at 475 and bold at 600, but the composed
fonttokens stopped at three weights. All five body sizes carrysemi-boldin all four line heights now, twenty tokens, so the scale has no gap to step around.--semantics-surfaces-ring-thickness, one token for the gap around something that overlaps. A badge over its anchor, an avatar over its neighbor, a timeline dot over its line, a step marker over its track: four components draw the same ring in the surrounding background so the shape in front stays readable. They took the value from two different primitives and called it two different things. The color stays in the components on purpose: it has to be the background actually behind the element, which they read from--context-parent-background-color, and avar()at token level would freeze against:root.invalidis declared on every input. Seventeen of the twenty-six did not have it, while the message was already appearing:nldd-formwrites the attribute and annldd-validation-listreads it straight off the control. It worked because something else reached in for it, which is the problemerror-messagehad. It is deliberately not drawn on a checkbox, a radio button or a switch: a red ring around one box says that option is wrong, while the question is simply unanswered. Not drawing it is no reason to stay silent, soaria-invalidgoes on the element assistive software meets, the same onedescribedTarget()already picks.nldd-formmarks a field invalid the moment the platform says so. Without something joiningsetValidityand the attribute, a control whose validation list refused a value blocked the submit while nothing on screen said why. The browser already picks the moment, firinginvalidon every failing control on submit, so the form listens in the capture phase. From that first verdict the mark follows the value both ways, and every field is judged at once rather than only the ones that failed. The native bubble goes with it: Safari put its own tooltip above the field carrying the text that already stood under it. Cancelling that event also cancels the browser's move to the first failing field, so the form makes it itself, once per round: without it a failed submit leaves focus where it was, which for anyone not looking at the screen is indistinguishable from nothing having happened. A reset takes the marks off again along with the values, because a reset fires noinputand a field would otherwise keep a mark over a value the user just cleared.requiredwhere it was missing.nldd-dropdown,nldd-search-field,nldd-combo-box,nldd-checkbox,nldd-switch,nldd-checkbox-field,nldd-switch-field,nldd-segmented-controlandnldd-toggle-button-groupdid not have it. On a choice group it goes to the items, where the platform reads it: one required radio makes the whole group required and the browser writes its own message in the user's language. Radio mode only, because the same attribute on a checkbox means that box has to be ticked and HTML has no way to say "at least one of these";aria-requiredgoes on regardless and DEV says that nothing enforces it.nldd-number-fieldandnldd-stepperstay without: their value defaults to 0, so an empty one does not exist there.pattern,minlengthandmaxlengthon the text fields. No input had them, so consumers fell back on their own JavaScript for something HTML has done for fifteen years, blocking the submit included. They are onnldd-text-field,nldd-password-field,nldd-search-fieldandnldd-combo-box. The multi-line field gets the two lengths only, because HTML has nopatternon a textarea.width="fit-content"onnldd-date-fieldandnldd-time-field. Both hold room at their end for a validation icon, so the field does not resize the moment it turns valid or invalid. Where a field is never validated that room is a visible gap.fit-contentkeeps only the air the text needs and takes the full slot back the moment a validation state does arrive, growing at the trailing edge so the date stays where it was. Opt-in, because only the consumer knows whether a field is ever validated.type="form"onnldd-list, for rows you fill in rather than walk through. A row of a label and a field, a switch, a menu button, or a value you cannot change. Semantically the same aslistand visually unchanged; what falls away is the keyboard of a list you walk through. No arrow navigation and no tab stop on the row: Tab goes straight to the controls, in source order. Rows that ARE actions contradict the type and warn in development. SwiftUI draws the same line betweenListandForm.Escape, from a control back onto its row. One level up, where Shift+Tab is one step back. It works wherever you are in the row and puts the arrow keys back in reach. It claims the key at exactly that one level: on the row it claims nothing, so Escape carries on to whatever holds the list, and a control showing a menu keeps the first press for closing that menu.
lineonnldd-timeline-track-cell, for a step that opens a group. A row that opens a group of its own is not the end of the going, so it drew an open track downward while there was progress below it.linenames the halves you have covered; the other half is track still ahead, andnonecovers neither. It is about fill, not place: which halves are drawn stays withposition, except that calling a half covered draws it.An inline
svgin the slot ofnldd-image. Only animgand apicturewere styled to fill the media box, so a drawing landed there at whatever size it brought along. It gets the same reset now, withoutobject-fit, which does nothing on an svg because its ownviewBoxdoes the fitting. A drawing gets the box, the ratio and the caption a photo gets, and keeps its own colors.no-tabon six more components.nldd-link,nldd-checkbox,nldd-radio-button,nldd-switch,nldd-toggle-buttonandnldd-avatarcarry the propertynldd-buttonandnldd-icon-buttonalready had:tabindex="-1"on the control in the shadow root, so a container that runs its own focus can take it out of the tab order.Changed
nldd-toolbar-titleandnldd-top-title-baris semi-bold instead of medium. Both sizes of the toolbar title, and in the title bar both the plain title and the one step smaller title that carries a subtitle. The subtitles stay regular.Fixed
required,pattern,minlengthandmaxlengthnow stop a submit. They are handed to a control inside the shadow root, and that control is not a member of the form around the component: the form sees the host, the host sees the control, and nothing joined the two. The form-associated mixin reports the control's verdict as the host's own now, which fixes every input that renders one. A group (nldd-segmented-control,nldd-toggle-button-group) and the editors have no such control and are unchanged. Two consequences: a form that used to go through with an empty required field now stops, and outside annldd-formthe browser shows its own bubble, the way it does for any form control.A validation list and a native constraint no longer erase each other.
setValiditywrites the whole set of flags at once, so whoever wrote last won: a list reporting its failing rule cleared the field'srequired, and the next render cleared the rule. Both go throughsetCustomValiditynow, which the mixin keeps beside the native flags rather than in place of them. A plain native input answers to the same method, so a list outside this system's inputs still works.A validation list keeps listening after its field is moved. Taking the element out of the DOM and putting it back, which is what a consumer portalling a sheet to
document.bodydoes, takes the input listener with it. Reconnecting found the same control as before and concluded there was nothing to wire up, leaving the list reacting to nothing. It now also checks whether it still has an observer.An empty
nameorwidthno longer reaches the DOM. Every input carriedname=""and half of themwidth="", on every instance in every consumer app. Twenty-two components reflect them only when they hold something now.The input of
nldd-number-fieldkeeps its minimum width. Three rules hung on:host([width]), and that selector always matched, because an emptywidthreflected to the DOM. Underneath sat a real bug: the base rule gives the input amin-widthand the always-matching rule overwrote it with 0, takinghide-spin-buttonsdown with it, which sets 80 pixels and got 0.The description of a field reaches the screen reader. It arrived at 3 of the 26 inputs, measured against the accessibility tree: the field wrote
aria-describedbyon the input in its own shadow root while the ids it named stood in the document, and an IDREF is looked up in the tree of the element that carries it. The elements themselves go through now, viaariaDescribedByElements, and a mixin hands the question down through a wrapper to the component that knows which element inside itself is the control.The website title in
nldd-top-navigation-baris a 24 by 24 target. WCAG 2.5.8 asks that of anything you click, and this one was 20 high on a phone and 23 at medium. The row around it was 24, but that came from padding on the bar, which does nothing for the target inside it. The title carries its own minimum now, in both directions and whether or not it links anywhere.Avatars in a group overlap by a fifth of their diameter, whatever that diameter is. The overlap was a flat 8 pixels: 29% of the avatar at size 28 and 8% at size 96, so the same component read as a tight stack at one size and a row of separate circles at another. A fifth is what 8 was at the default size of 40, so a group at that size looks exactly as it did.
A form section stands 24 pixels from its neighbors on both sides. It carried padding, and padding does not collapse, so it added itself to the 16 the form already puts under the preceding child. It is a margin now: 16 and 24 beside each other are 24. On both sides rather than only above, because with a top margin alone the distance under a section depended on what happened to follow it.
The buttons of a form stand 24 pixels below it. Also in a form without sections, where the actions used to be one more gap like everything else. The end of a form is the end, not one more question.
A section heading sits 8 pixels above its fields instead of 16. It belongs to what is under it, and the same distance on both sides left it floating between the group above and the group below.
A form section spaces whatever you put in it. Its children leaned on a rule that only recognized one field followed by another, so a button or a paragraph inside a section got no room and every caller added it themselves. The section uses the same rhythm as the form around it now, and asks whether something visible follows rather than what the thing is.
A field label sits four pixels above its control instead of three, in the flat type rather than the tight one. Three was close enough to read as attached and far enough to look like a mistake, and the tight leading pulled a wrapping label onto itself.
One gap between everything in a form. Two fields in a row used to sit ten pixels apart instead of sixteen, and it took two container queries and four
:not()s to say so. It bought six pixels per pair, while a toggle group or a combo box stood ten pixels from the next label. A field, a section, a button, a paragraph: whatever follows another thing stands the same distance from it now, and a section or the buttons of a form stand a step further off. The three gap tokens are two, named after what they separate:--semantics-forms-fields-gapand--semantics-forms-sections-gap.--semantics-forms-gapand--semantics-forms-gap-tightare gone; nothing outside this package used either.nldd-form-sectionno longer draws lines around itself. A section carries a heading, and a rule above and below says the same thing a second time. What gave it away was the suppressing: no line on the first child, none between two adjacent sections, none on the last, and that last exception did not work, so every form with a submit button had a rule drawn right above that button. The padding stays and is what groups.A
closefrom an overlay stays with the overlay it came from.nldd-sheet,nldd-window,nldd-modal-dialogandnldd-popoverfiredclosewithbubbles: true, so an overlay inside another one reached the listener on the one around it. A consumer binding(close)on the outer overlay got the inner one's close as well and tore down what was still open. The event no longer bubbles. Anyone checkingevent.targetto work around this can stop.Escape closes what is on top, and nothing behind it. A combo box, a token field and anything on
nldd-popoverclosed on Escape and let the same press travel on, so a sheet or window behind them went too. They stop the press the waynldd-menualready did. With nothing open they claim nothing, so Escape still reaches the sheet.A list no longer takes the arrow keys out of a control in one of its rows. The keys were claimed by whichever list they bubbled through. A combo box in a row lost its menu on ArrowDown, and a text field lost its caret on Home. The list stands down now while the key comes from a control the row holds; what the row IS keeps answering, since a link, button or segment is not a control it holds.
A list is one tab stop from the outside again. The current row hands its controls a place in the tab order, and those places stayed handed out after focus left the list, so tabbing back in dropped you into the control you had left rather than on the row. Leaving the list parks them again.
The clear and picker buttons of a combo box sit in the middle again. Both are wrapped in a plain block, so the wrapper took the height of the line box it inherited and the button hung at the top of it: centered in a story, high in an app whose body sets a taller line-height. They are flex now, like the file, time and date fields.
A row let go outside itself no longer stays lit.
nldd-list-itemandnldd-list-item-segmentshowed the press onpointerdownand cleared it on apointerupon the row, which is not where the event lands when you release off the row. The release is watched on the window now, for as long as a press is running.nldd-boxandnldd-cardtake the width they are given. Annldd-celllines its content up on the left rather than stretching it, so a box or a card inside one shrank to its content: in a narrow cell that came down to a single letter with the title running vertically. As a block that already happened, as a flex item it did not.The scroll mode follows a split view that arrives late. A consumer that renders its split view behind a condition, such as a loading state or an empty state, takes it away and puts it back. While it was gone the derivation said
root, and the split view that comes back measures itself as what it already was, so it announces nothing: every pane kept scrolling as one document until a reload. A layer arriving or leaving now re-runs the derivation.A bar in a sheet keeps its back button. A split view hides the back button of a bar inside a pane, because the menu beside you is the way back. That hiding traveled down the whole pane, so a sheet standing in one lost the back button of its own bar. A sheet starts that context over.
The warning about a control a row cannot reach now fires, and names the control. It ran while the list rendered, and a slotted custom element has its shadow root by then but has not rendered into it, so the check found nothing. It waits for the control to have rendered, runs again for rows added later, and reports once. It stays quiet where nothing is wrong: a segment, a disabled or negatively-tabindexed control, and anything the component hides itself.
Lit no longer reports an update scheduled after an update completed.
nldd-list,nldd-list-item,nldd-collection,nldd-menu,nldd-menu-itemandnldd-navigation-split-viewset state fromfirstUpdated, from the firstslotchangeor fromupdated, which is a dev-mode warning on every page holding a list or a menu. What the first render needs is read from the light DOM before it now, and what can only be read afterwards is set out of the update cycle.A branch row that starts collapsed no longer warns. The row reads an absent
expandedas collapsed and emitsaria-expanded="false", so the warning asked for something that was already there. The warning aboutslot="children"outside atype="tree"list stays, and reports once per row instead of on every change.v0.8.83Compare Source
Highlights
nldd-text, a component for body text. New, and the one piece of typography that had no component: every line that is not a heading and not a block of prose. It offerssize(xxs through lg),weight,line-height,colorandhorizontal-alignment, all of them names for what the token scale already holds, so it cannot invent typography. Color follows the content channel a row, menu or table sets, so a line travels with the row it sits in. Reach fornldd-titlefor a heading,nldd-rich-textfor prose with its own rhythm.Every list answers to the arrow keys, and a row may hold more than one control. One list now behaves like the next: ArrowUp and ArrowDown walk the rows there is something to do with, Home and End jump to the ends, and the list as a whole is one tab stop. Within the current row Tab walks its segments and the controls in its cells, while those same controls in the other rows stay out of the tab order. Two types mean something else by the same keys, and you can see which before you touch one: a
listboxruns its keyboard from its search field, and areorderablelist moves rows.currentbesideselectedin a list row.currentis the row you are on,selecteda row you picked, and a list can show both. At rest they look the same, so existing lists do not change. The difference shows when focus is in the row: only the current row takes the accent fill, and it does so from a nestednldd-list-item-segmenttoo. Row states now sit on the same ladder as button states, with a rest, hover and press step for each, under the names the buttons already use. Seven tokens more.A color the system does not know, on a badge and an icon. Some colors are not a category but a fact: the jacket of a cable is blue whatever the house style does.
custom-coloronnldd-badgeandnldd-icontakes any CSS color value and wins overcolor, so the vocabulary ofcolorstays closed and one search shows where an app steps outside the palette. On the badge, what sits on the color turns white or black, whichever contrasts better.Thirty icons added, three redrawn. Added:
cpu,gpu,memory-chip,psu,pci-card,transceiver-module,rack-server,rack-servers,external-hard-drives,ssd-hard-drive,external-hard-drive,network-switch,kvm-switch,power-plug,lightning,snowflake,boxes-3,clipboard-bullet-list,kanban-columns,printer,shield-arrow-right-arrow-left,waving-crossing-lines,circle,circle-circle,minus-circle,circle-light,circle-circle-light,check-mark-circle-light,clock-lightandslash-circle-light. Redrawn:shield,shield-check-markandshield-lock.Breaking
database-disabledanddatabase-unavailableare nowdisabled-databaseandunavailable-database. The set puts the state in front everywhere else:new-namespace,no-priority,checked-small. Those two trailed it, and were the only aliases that did apart from the-lightweights, which name the drawing rather than a state. Both still point atcylinder-split-slash. Consumers on an old name change one string.nldd-list-item-actionis nownldd-list-item-segment. The component was not always an action: withouthref,buttonorcheckbox, and inside a listbox, it renders as a plain container. What it always is, is a piece of the row with its own hit area and its own fill, which is what the docs had been calling it all along. Consumers rename the element and the import path, and the attributes stay the same. The host classes follow:has-leading-segmentandhas-trailing-segment.Every list answers to the arrow keys, and
arrow-navigationis gone. Whether the arrow keys work is what a reader may assume about every list, not a setting per list, and as a setting it was unknowable from the outside. Consumers drop the attribute. Leaving it in does nothing, and warns in dev.nldd-boxpaints withbackground, like every other surface. The attribute wasvariant, which in this system is the word for what a component is, not for which surface it draws.nldd-app-view,nldd-page,nldd-split-view-paneandnldd-cardall call thatbackground. Sovariant="base|tinted|critical"is nowbackground="base|tinted|critical". One thing to know: a box starts ontintedwhere a card starts onbase, because a box stands out from the page and a card sits on it. Consumers change one string per box.The plus badges are named after what they make.
add-namespaceis nownew-namespace,add-k8sandadd-kubernetesarenew-k8sandnew-kubernetes, andadd-text-documentis gone in favor ofnew-text-document. A namespace, a cluster and a document are made, not added.addstays for putting something that exists into something else, which is whyadd-plugin,add-user,add-locationandadd-emojiare unchanged. Consumers on an old name change one string.Added
Eighteen icons for a data center: the hardware and the network.
cpu,gpu,memory-chip,psu,pci-card,transceiver-module,rack-server,rack-servers,external-hard-drives,ssd-hard-drive,external-hard-drive,network-switch,kvm-switch,power-plug,lightning,snowflake,waving-crossing-lines(network-patch-mapping) andshield-arrow-right-arrow-left(firewall), with the aliasesserver,servers,memory,ram,processor,power,cooling,airco,hard-drive,nic,network-interface-card,sfpandenergy. Two are named after what they draw rather than what they came in for:network-switch, becauseswitchon its own is the verb, andsnowflake.arrow-left-rightgets that verb as an alias:switchandswap, for moving between two things. There are two racks:rack-serversdrawn flat on for a diagram, andrack-serverat an angle for a row, where three stacked bays turn into three stripes.serverpoints at the angled one,serversat the flat one.waving-crossing-linesdraws the runs themselves, sweeping from one side to the other and crossing on the way, rather than the nodes they connect, which is what a patch panel looks like from the front. Two of them are a pair:external-hard-driveis one unit,external-hard-drivesthe stack of three, aliasedstorage. The firewall shield carries the traffic it filters, two arrows passing each other, so it says what it does instead of only saying "safe".Eight icons for getting things done. The three task states read as one series:
circlefor to do andcircle-circlefor doing, beside thecheck-mark-circlethe set already had, aliased asto-do,doinganddone.minus-circleis in the set as well: the same ring with a bar across it.kanban-columnsdraws a board of work in columns (kanban), andcircle-grid-2x2-top-left-check-markgets the aliasall-tasks.circledoubles asno-priority: the same ring aslow-prioritythroughhigh-prioritywith nothing in it, so that column keeps reading as a count from zero up. Four of the state icons come in a light weight drawn for 32 pixels:circle-light,circle-circle-light,check-mark-circle-lightandclock-lightkeep the 2-pixel line on a 32 view-box instead of a 24 one, so the line stays 2 pixels at the size they are drawn for rather than growing to 2.7. Use them where a state icon leads a row at 32, and reach for the regular set below that. Aliased asto-do-light,doing-lightanddone-light.Four icons outside those two groups, and one alias.
boxes-3for a stock of things you own (inventory),clipboard-bullet-listfor that same list read as a checklist (inventory-alt),printerfor printing (print), andslash-circle-lightin the same light weight as the state icons (blocked-light).brackets-ellipsisgets the aliasnamespace, the meaning the brackets with the ellipsis carry wherever the console uses them.color="inherit"onnldd-badge. Fill with the content color around it (--context-content-color, otherwisecurrentColor), the meaning it already has onnldd-avatar, so a badge travels with the line it sits in.disabledworks on a row and on a link. A segment could be switched off and a row could not, while a row can just as well be the button or the checkbox. Abuttonorcheckboxrow takes the real off state and dims. A link getsaria-disabledand a blocked click, because an anchor cannot be switched off the way a button can, and that goes for a link segment as well: theredisabledused to do nothing at all. Hover and press do nothing there, and the arrow keys skip it: a disabled control is not focusable, so a stop there would swallow the key. That already applied to a disabled segment, so a row whose only segment is off is no longer a stop either.A
currentsegment makes its whole row current. A row built from segments has no link of its own, soaria-current="page"belongs on the segment that holds the link. The row reads it off its own segments and paints itself, so it is written once. Reading, not writing: the attributes stay the consumer's.currentalso appears in the controls ofnldd-list-itemandnldd-list-item-segmentnow.A ticked row is painted, the same as a ticked segment. A row that is the checkbox itself took no fill, so the same list looked different depending on where the tick sat. A ticked row is a row you picked, so it paints what
selectedpaints, with the same hover and press steps. Only together withcheckbox.A segment stays lit while what it opened is on screen.
expandedon a segment setaria-expandedand turned a chevron, but left the segment looking untouched, so a menu hanging off one row floated over the list without saying which row it belonged to. It now paints the same fill as a picked row (--components-list-item-is-expanded-background-color, two steps above hover), and after the hover rule, so the pointer cannot dim it while the menu is open.no-tabonnldd-button. The same propertynldd-icon-buttonalready had: take the button out of the tab order (tabindex="-1"on the button in the shadow root) because a container around it manages focus itself. Annldd-listsets it on the buttons in the rows that are not the current one. Mouse and script still reach the button.Enter follows the row in a tree. It activates the row's own link or button, or on a branch built from segments the first one that is not the chevron. Space folds, the same as Left and Right. Enter used to fold, so a branch you could reach with the arrow keys could not be opened without hunting for a segment with Tab.
readonlyonnldd-combo-box. The value stays readable and submits with the form, and the control that would change it is gone rather than dimmed.keyboardandenter-keyon the text fields. They setinputmodeandenterkeyhint, so a phone shows the right keys and the right label on the return key.Changed
inboxis nowtray, and a set of aliases moved with the way the set names things. The file says what it draws, a tray with a slot, andinboxstays as the alias for what it is used for. New aliases:time-lightonclock-light,locked-databaseoncylinder-split-badge-lock,checked-text-documentonfile-text-badge-check-mark,companiesonapartment-building-2,add-extensionandadd-moduleonpuzzle-piece-badge-plus(the plain piece already answered to all three words, the plus badge only toplugin), and the directory vocabulary is carried through the folders it was missing from:new-directory,folders,directories,open-folderandopen-directory.The three shields are redrawn on one silhouette.
shield,shield-check-markandshield-lockshared a pointed crest built from arcs, and now stand on the same flat-topped, rolled-edge shield asshield-arrow-right-arrow-left. That shield is wider at the top and flatter, and that is the reason for it: more fits on it. A check mark, a lock or two arrows passing each other need room, and on a pointed crest such a figure runs into the slanted edges. Four shields that differ only in what they carry, rather than a family that splits the moment a fourth arrives.A read-only field takes its controls away instead of dimming them. A dimmed control invites a click that does nothing. The field keeps its value and its label, and drops the spinner, the picker and the clear button.
A subtitle in
nldd-titleis one step larger. It sat two steps under the title, which read as a caption rather than as the line that belongs to it.Fixed
The text on a colored fill is picked on luminance now, and clears 4.5:1.
--semantics-content-contrast-colordecided between black and white on OKLCH lightness, which is perceptual and parts ways with the luminance WCAG measures, so a saturated mid blue got white text at 3.68:1. The threshold is now the relative luminance where black and white give the same contrast, Y = (sqrt(21) - 1) / 20, which makes the picked color at least 4.58:1 whatever it lands on. Where the browser hascontrast-color(), that answers instead. This reaches every component that puts content on a fill it was handed:nldd-badge,nldd-avatar,nldd-step-indicator,nldd-keyboard-shortcutand the timeline cell.Switching a row off now moves the tab stop with it. A row that carried the list's single tab stop kept it after
disabledlanded on it, so Tab reached a row that answers to nothing. The arrow keys already skipped it, and an unrelated change happened to repair it, which is the worst kind of bug: it fixes itself while you look at it. The row re-runs the roving when its owndisabledchanges, and the list watches the attribute too.A read-only combo box no longer announces a list it cannot open. The picker button and the menu were already gone, but the input kept
role="combobox"witharia-haspopup="listbox",aria-autocompleteandaria-expanded. It is a text field with a value in it, and it now says so.color="inherit"on a badge or an avatar could paint white on white. The fill came from the content channel around it, while the contrast flip that decides between black and white text read the inheritedcolor. Those are the same value in most places, so it went unnoticed until a row set the channel to white over dark text: the badge then took a white fill and, from the dark inherited color, white text as well. Both components now set their owncolorto the fill first, the waycustom-coloralready did, so the flip is computed against what is actually painted.An overlay is where the app's layout context ends. A sheet or dialog inherited the app's scroll mode, so a short page inside an overlay could float its sticky footer mid-screen. An overlay now starts its own context.
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.
7032395da9335a5a9248Update dependency @nldd/design-system to ^0.8.83to Update dependency @nldd/design-system to ^0.8.84335a5a9248efa08fbcc2Update dependency @nldd/design-system to ^0.8.84to Update dependency @nldd/design-system to ^0.8.85efa08fbcc29134f31b7aUpdate dependency @nldd/design-system to ^0.8.85to Update dependency @nldd/design-system to ^0.8.86View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.