Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions indeks-css/css/components/form/combobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,19 @@
- __listbox popover-panel med forslag
- __option to-linjers list item (checkmark + label + description)
- __option-* check / label / description
- __no-hits melding når filteret gir 0 treff */
- __no-hits melding når filteret gir 0 treff

Selektorstrategi (som text-field.css / field.css): der web componenten setter
et stabilt kjøretids-anker bruker vi det som PRIMÆR selektor og beholder BEM-
klassen som backup — `:where(<anker>, .ix-combobox__<del>)`:
- listbox → [role='listbox'] (settes i IxCombobox._wire)
- option → [role='option'] (+ [data-active]/[aria-selected] tilstand)
- toggle → .ix-text-field > button (eneste knapp i feltet)
- chips → [data-field='chips'] (WC leser wrapperen slik)
BEM-klassene beholdes uansett: de er querySelector-hooks i JS, og fungerer som
no-flash-anker før connectedCallback rekker å sette role. Delene UTEN trygt
anker (__option-check/label/description, __no-hits — role=status er tvetydig
mot den skjulte results-regionen) forblir rene klasse-selektorer. */

:where(ix-combobox, .ix-combobox) {
display: block;
Expand All @@ -23,15 +35,15 @@
/* ── Chips-wrapper (kun multiselect) ────────────────────────────────────────
Chipene selv er <data class="ix-chip" data-removable> og styles av chip.css.
Her styles kun oppsettet: flyt over flere linjer ved mange valg (1.4.10). */
:where(.ix-combobox__chips) {
:where(.ix-combobox [data-field='chips'], .ix-combobox__chips) {
display: flex;
flex-wrap: wrap;
gap: var(--ix-spacing-xs);
margin-block-end: var(--ix-spacing-xs);
}

/* Tom wrapper skal ikke ta plass før noe er valgt. */
:where(.ix-combobox__chips:empty) {
:where(.ix-combobox [data-field='chips']:empty, .ix-combobox__chips:empty) {
display: none;
}

Expand All @@ -45,7 +57,7 @@
position: relative;
}

:where(.ix-combobox__toggle) {
:where(.ix-combobox .ix-text-field > button, .ix-combobox__toggle) {
all: unset;
box-sizing: border-box;

Expand Down Expand Up @@ -78,7 +90,7 @@
padding-inline-end: calc(var(--ix-spacing-sm) + 1.5em);
}

:where(.ix-combobox__toggle)::after {
:where(.ix-combobox .ix-text-field > button, .ix-combobox__toggle)::after {
content: '';
width: 1em;
height: 1em;
Expand All @@ -97,22 +109,25 @@
}

/* Chevron peker opp når lista er åpen. Host [data-open] settes av web componenten. */
:where(ix-combobox[data-open], .ix-combobox[data-open]) .ix-text-field > button::after,
:where(ix-combobox[data-open], .ix-combobox[data-open]) .ix-combobox__toggle::after {
transform: rotate(180deg);
}

:where(.ix-combobox__toggle:focus-visible) {
:where(.ix-combobox .ix-text-field > button:focus-visible, .ix-combobox__toggle:focus-visible) {
outline: var(--ix-outline-default);
outline-offset: calc(-1 * var(--ix-outline-offset-default));
}

/* Knappen er en gjennomsiktig overlay oppå inputen — input eier ramme og flate. */
:where(.ix-text-field) > .ix-combobox__toggle {
:where(.ix-combobox .ix-text-field > button, .ix-text-field > .ix-combobox__toggle) {
background-color: transparent;
border: none;
}

/* Disabled / read-only felt toner ned chevronen sammen med inputen. */
:where(.ix-text-field) > input:disabled ~ button,
:where(.ix-text-field) > input:read-only ~ button,
:where(.ix-text-field) > input:disabled ~ .ix-combobox__toggle,
:where(.ix-text-field) > input:read-only ~ .ix-combobox__toggle {
color: var(--ix-color-foreground-main-disabled);
Expand All @@ -127,7 +142,7 @@
popover-API, ingen avhengighet, hele browserslist). Panelet ligger
position: fixed i viewport-koordinater. Ingen shadow-token finnes ennå, så
panelet løftes med tydelig kant + flate, som tooltip. */
:where(.ix-combobox__listbox) {
:where(.ix-combobox [role='listbox'], .ix-combobox__listbox) {
position: fixed;
margin: 0;
padding: var(--ix-spacing-2xs);
Expand All @@ -137,14 +152,20 @@
border: var(--ix-border-width-default) solid var(--ix-color-border-main-default);
border-radius: var(--ix-border-radius-sm);

/* Egen min-bredde uavhengig av feltet. _position() (IxCombobox.ts) setter
inline width = feltbredden; når feltet er smalt (f.eks. landvelgeren i
phone-number-field viser bare `+47`) ville lista blitt for smal for
landnavn. min-width vinner over inline width, så lista forblir lesbar.
16rem (256px) < 320px mobil, og JS klemmer left ≥ 8px, så ingen overflow. */
min-width: 16rem;
max-height: 16rem;
overflow-y: auto;
z-index: var(--ix-z-index-overlay);
}

/* Lukket: web componenten setter hidden. [hidden] i seg selv gir display: none,
men vi er eksplisitte her siden panelet ellers har display fra listen. */
:where(.ix-combobox__listbox[hidden]) {
:where(.ix-combobox [role='listbox'][hidden], .ix-combobox__listbox[hidden]) {
display: none;
}

Expand All @@ -153,7 +174,7 @@
plass alltid (checkmark er visibility:hidden når ikke valgt), slik at label
ikke hopper når valgt tilstand endres. Valgt vises med checkmark (struktur)
+ fetere label — ikke farge alene (WCAG 1.4.1). */
:where(.ix-combobox__option) {
:where(.ix-combobox [role='option'], .ix-combobox__option) {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
Expand All @@ -173,12 +194,12 @@
componenten — IKKE :hover alene, for tastatur og mus skal se likt ut.
Lyseblå flate; valgt tilstand vises i tillegg med checkmark (ikke farge alene,
WCAG 1.4.1). */
:where(.ix-combobox__option:hover),
:where(.ix-combobox__option[data-active]) {
:where(.ix-combobox [role='option']:hover, .ix-combobox__option:hover),
:where(.ix-combobox [role='option'][data-active], .ix-combobox__option[data-active]) {
background-color: var(--ix-color-fill-info-subtle);
}

:where(.ix-combobox__option[aria-disabled='true']) {
:where(.ix-combobox [role='option'][aria-disabled='true'], .ix-combobox__option[aria-disabled='true']) {
color: var(--ix-color-foreground-main-disabled);
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
cursor: not-allowed;
Expand Down Expand Up @@ -223,12 +244,12 @@
}

/* Valgt option viser krysset (begge moduser). */
:where(.ix-combobox__option[aria-selected='true']) .ix-combobox__option-check {
:where(.ix-combobox [role='option'][aria-selected='true'], .ix-combobox__option[aria-selected='true']) .ix-combobox__option-check {
visibility: visible;
}

/* Valgt option får litt tyngre label så tilstanden ikke hviler på farge alene. */
:where(.ix-combobox__option[aria-selected='true']) .ix-combobox__option-label {
:where(.ix-combobox [role='option'][aria-selected='true'], .ix-combobox__option[aria-selected='true']) .ix-combobox__option-label {
font-weight: var(--ix-font-weight-medium);
}

Expand All @@ -252,7 +273,7 @@
Playwright kjører med reducedMotion: 'reduce'; stopp chevron-rotasjonens
transition slik at snapshots blir deterministiske. */
@media (prefers-reduced-motion: reduce) {
:where(.ix-combobox__toggle)::after {
:where(.ix-combobox .ix-text-field > button, .ix-combobox__toggle)::after {
transition: none;
}
}
1 change: 1 addition & 0 deletions indeks-css/css/components/form/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@import './date-field.css';
@import './form.css';
@import './label.css';
@import './phone-number-field.css';
@import './radio-group.css';
@import './select.css';
@import './validation-message.css';
113 changes: 113 additions & 0 deletions indeks-css/css/components/form/phone-number-field.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* PhoneNumberField — telefonnummer med landvelger.

Sammensatt komponent: en gruppe (role="group") som binder sammen en
eksisterende <ix-combobox> (landkode) og et <ix-field>-tekstfelt (nummer)
under én felles label og én felles valideringsmelding.

ARIA-lim-mønster (som ix-radio-group / ix-checkbox-group): web componenten
<ix-phone-number-field> kobler eksisterende native elementer i light DOM —
den eier ikke innholdet. Selve feltene styles av combobox.css og
text-field.css; denne filen styrer KUN gruppe-oppsettet og side-ved-side-
layouten. Legend/description/error gjenbruker samme data-field-navn og
samme visuelle språk som field.css / radio-group.css. */

:where(ix-phone-number-field, .ix-phone-number-field) {
display: flex;
flex-direction: column;
gap: var(--ix-spacing-2xs);
padding: 0;
border: 0;
}

/* ── Legend (felles label) ──────────────────────────────────────────────────
Samme uttrykk som radio-group sin legend. Ikonet foran (f.eks. lås ved
read-only) farges rødt ved feil, som ellers i skjema. */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='legend'] {
display: inline-flex;
align-items: center;
gap: var(--ix-spacing-2xs);
font-size: var(--ix-font-size-md);
font-weight: var(--ix-font-weight-medium);
color: var(--ix-color-foreground-main-default);
}

:where(ix-phone-number-field[data-state='error'], .ix-phone-number-field[data-state='error'], ix-phone-number-field[aria-invalid='true']) [data-field='legend'] ix-icon {
color: var(--ix-color-foreground-danger-default);
}

/* ── Description (felles hjelpetekst) ───────────────────────────────────────── */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='description'] {
font-size: var(--ix-font-size-md);
color: var(--ix-color-foreground-main-subtle);
line-height: 150%;
margin: 0;
}

/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='description']:empty {
display: none;
}

/* ── Error (felles valideringsmelding) ──────────────────────────────────────
role="status"/aria-live settes av web componenten. Tom melding skjules
visuelt med :empty — samme mønster som [data-field="error"] i ix-field. */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='error'] {
display: inline-flex;
align-items: center;
gap: var(--ix-spacing-2xs);
font-size: var(--ix-font-size-md);
color: var(--ix-color-foreground-danger-default);
}

/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='error']:empty {
display: none;
}

/* ── Feltene side ved side ──────────────────────────────────────────────────
Landvelger og nummerfelt ligger alltid på samme rad — også på 320 px. Vi
bruker `nowrap` (ikke `wrap`) fordi landvelgeren nå er smal (kun kallekode
`+47` vises som valgt verdi), så begge feltene får plass selv på den minste
skjermen. Nummerfeltet tar resten (flex: 1, min-width: 0), så gruppa aldri
tvinger horisontal scroll (WCAG 1.4.10). Ingen fast bredde på selve gruppa. */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='items'] {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-start;
gap: var(--ix-spacing-sm);
}

/* Landvelger: smal, fast bredde. Vi MÅ sette en eksplisitt `flex-basis` (ikke
`auto`) — med `auto` arver kolonnen inputens intrinsiske bredde (~20 tegn),
og siden den ikke skal krympe (`flex-shrink: 0`) ville feltet blitt altfor
bredt. Kolonnen viser bare kallekoden (`+47`), så 5.5rem gir rom til koden +
chevron-reservasjonen. `min-width: 0` fjerner flex-itemets default
`min-width: auto`, ellers kan det brede barnet (comboboxens input) presse
kolonnen forbi basisen — samme grunn som nummerfeltet under. Selve
nedtrekkslista har egen min-width (combobox.css) så landnavn er lesbare der. */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='country'] {
flex: 0 0 5.5rem;
min-width: 0;
}

/* Nummerfelt: tar resterende plass. min-width: 0 lar det krympe under sin
intrinsiske bredde i stedet for å tvinge horisontal scroll. */
:where(ix-phone-number-field, .ix-phone-number-field) [data-field='number'] {
flex: 1 1 auto;
min-width: 0;
}

/* ── Disabled ────────────────────────────────────────────────────────────────
Selve feltene toner seg ned via sin egen disabled-styling (combobox/text-
field). Her demper vi legend/description på gruppenivå slik radio-group gjør,
uten å blokkere pointer-events på feltene (de håndterer det selv). */
:where(ix-phone-number-field[data-state='disabled'], .ix-phone-number-field[data-state='disabled']) [data-field='legend'],
:where(ix-phone-number-field[data-state='disabled'], .ix-phone-number-field[data-state='disabled']) [data-field='description'] {
opacity: var(--ix-opacity-disabled);
}

/* ── ReadOnly ──────────────────────────────────────────────────────────────── */
:where(ix-phone-number-field[data-state='readonly'], .ix-phone-number-field[data-state='readonly']) [data-field='legend'] {
color: var(--ix-color-foreground-main-read-only);
}
Loading
Loading