@layer theme { /* generic .dropdown-menu popup: nothing in luci-base emits it — coverage vocabulary, since * stock luci-theme-bootstrap styles it and a third-party app may emit it */ .dropdown-menu { background-color: var(--fs-panel); border-color: var(--fs-border); } .dropdown-menu a { color: var(--fs-text); text-shadow: none; } .dropdown-menu a:hover, .dropdown-menu a:focus-visible { background: var(--fs-panel2); color: var(--fs-accent); } /* the outer div IS the button, class = ui.js's SELECTED mode; unguarded this erased that * variant's fill while its ink survived — measured page-on-page at 1.06-1.13:1. */ .cbi-page-actions .cbi-dropdown:not(.btn):not(.cbi-button) { border: 0; border-radius: var(--fs-radius); background: transparent; } /* Apply is the one variant with no fill of its own (theme/55-buttons.css gives the rest theirs); painted by specificity, not source order */ .cbi-page-actions .cbi-dropdown.cbi-button-apply, .cbi-page-actions .cbi-dropdown > .cbi-button { background: var(--fs-accent); border: 0; color: var(--fs-on-accent); } .cbi-page-actions .cbi-dropdown.cbi-button-apply:hover, .cbi-page-actions .cbi-dropdown > .cbi-button:hover { filter: var(--fs-hover-lift); } /* ---- cbi-dropdown widget: the JS select used across all forms ---- */ /* machinery absorbed from base, driven by ui.js attributes; the state machine below is * UNFLAGGED, stating openly what upstream's !important flags resolved — docs/css.md * "Dropdown: the display state machine, unflagged" has the specificity table. */ .cbi-dropdown { display: inline-flex; cursor: pointer; height: auto; position: relative; padding: 0; } .cbi-dropdown img { /* constrain a vector icon of any intrinsic size to the text line */ max-height: 1.6em; width: auto; } .cbi-dropdown > ul { margin: 0; padding: 0; list-style: none; overflow-x: hidden; overflow-y: hidden; display: flex; width: 100%; } /* clears the button's own side inset so the label starts on its text, unflagged (0,3,1) over the rule above's (0,1,1) */ .cbi-dropdown.btn > ul:not(.dropdown), .cbi-dropdown.cbi-button > ul:not(.dropdown) { margin-block: 0; margin-inline: var(--fs-space-3); } .cbi-dropdown.btn.spinning > ul:not(.dropdown), .cbi-dropdown.cbi-button.spinning > ul:not(.dropdown) { margin: 0; } .cbi-dropdown > ul.preview { display: none; } /* no `display` here: `.open` states its own below, `.more` is none until the showers flip it — one declaration per state or the audit's same-selector shadow check fires */ .cbi-dropdown > .open, .cbi-dropdown > .more { flex-grow: 0; flex-shrink: 0; flex-direction: column; justify-content: center; text-align: center; } /* currentColor: the variant's own ink, 4.6-5.4:1 on every button fill measured; --fs-border is 1.45:1 */ .cbi-dropdown.btn > .open, .cbi-dropdown.cbi-button > .open { border-inline-start: 1px solid currentColor; } .cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[placeholder] { display: none; } .cbi-dropdown > ul > li { display: none; font-size: var(--fs-type-field); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; /* ellipsis did nothing until min-width: 0 — a flex item's automatic minimum floors it at its * own content width — docs/css.md "Dropdown: ellipsis needs `min-width: 0` first". */ min-width: 0; flex-shrink: 1; flex-grow: 1; align-items: center; align-self: center; color: inherit; } .cbi-dropdown > ul > li .hide-open { display: initial; } .cbi-dropdown > ul > li .hide-close { display: none; } /* a button dropdown's label is a button label, not a form field's 16px value; block, not the * state machine's flex, since text-overflow only draws its "…" on a block container */ .cbi-dropdown:is(.btn, .cbi-button) > ul:not(.dropdown) > li[display] { display: block; font-size: inherit; } /* the chip divider on a BUTTON dropdown's preview; the form dropdown zeroes it below */ .cbi-dropdown > ul > li[display]:not([display="0"]) { border-inline-start: var(--fs-hairline); } .cbi-dropdown[empty] > ul { max-width: 1px; } .cbi-dropdown > ul > li > form { display: none; margin: 0; padding: 0; pointer-events: none; } .cbi-dropdown > ul > li img { vertical-align: middle; margin-inline-end: .25em; } .cbi-dropdown > ul > li > form > input[type="checkbox"] { margin-block: 0; margin-inline: 0 var(--fs-space-1); } /* ---- the display state machine, on specificity (docs/css.md has the per-selector table) ---- */ /* a closed optional dropdown with an empty value renders EMPTY unflagged too: the placeholder * hider out-specifies the generic shower. :not([open]) keeps this out of the open list. */ .cbi-dropdown > ul > li[display], .cbi-dropdown[open] > ul.preview, .cbi-dropdown[open] > ul.dropdown > li, .cbi-dropdown[multiple] > ul > li > label, .cbi-dropdown[multiple][open] > ul.dropdown > li, .cbi-dropdown[multiple][more] > .more, .cbi-dropdown[multiple][empty] > .more, .cbi-dropdown:not([open]):not(.btn):not(.cbi-button) > ul > li[placeholder][display] { flex-grow: 1; display: flex; } .cbi-dropdown[empty] > ul > li, .cbi-dropdown[optional][open] > ul.dropdown > li[placeholder] { display: block; } .cbi-dropdown[multiple][open] > ul.dropdown > li > form { display: flex; } .cbi-dropdown[open] > ul.dropdown > li .hide-open { display: none; } /* min-width: a CHILD with an inline width overflows, wrapping the row cannot reach it — * form.js's RichListValue assumes an unbounded list (issue #15). */ .cbi-dropdown[open] > ul.dropdown > li .hide-close { display: initial; min-width: 0 !important; } .cbi-dropdown[open] > ul.dropdown > li:last-child { margin-bottom: 0; } .cbi-dropdown[open] > ul.dropdown > li[unselectable] { opacity: .7; } .cbi-dropdown:not(.btn):not(.cbi-button) { background: var(--fs-panel2); border: var(--fs-hairline); border-radius: var(--fs-radius); color: var(--fs-text); min-height: var(--fs-ctl-h); /* fixed height: without it a closed dropdown grows to its tallest child (Save & Apply's * 40px button, measured 38 -> 42px on the gallery); --fs-ctl-h, since the min-height floor always won */ height: var(--fs-ctl-h); font-size: var(--fs-type); transition: var(--fs-field-transition); /* absorbed from base/30-forms.css; NOT the dynlist's numbers (240/440) — one value vs a column of them */ min-width: 210px; max-width: 400px; width: auto; } .cbi-dropdown.btn, .cbi-dropdown.cbi-button { padding: 0; } .cbi-dropdown:not(.btn):not(.cbi-button):hover { border-color: var(--fs-dim); } .cbi-dropdown:focus, .cbi-dropdown[open] { border-color: var(--fs-accent); box-shadow: var(--fs-focus-ring); outline: none; } /* `.cbi-dropdown > ul.dropdown > li` is the same row on a BUTTON dropdown, excluded by * :not(.btn):not(.cbi-button) below — one menu, one row, but two selectors. */ .cbi-dropdown:not(.btn):not(.cbi-button) > ul > li, .cbi-dropdown > ul.dropdown > li { padding: var(--fs-space-2) var(--fs-space-3); color: var(--fs-text); font-family: var(--fs-font-sans); /* a menu row is not a button label: the open list sits inside the button and would inherit its 600 and 1.1 line (a plain dropdown's rows: 400, 1.5) */ font-weight: var(--fs-weight-normal); line-height: var(--fs-leading); /* a grid step, not calc(--fs-type * --fs-leading) (19.5px) — a half-pixel box edge is what the scale exists to stop */ min-height: var(--fs-space-5); } /* a MULTI-select wraps items in