openvibers/openvibe.shared kit-layout.css
kit-layout.css
| 1 | /* openvibe-shared/kit-layout.css: the Kit's fluid scale, its layout primitives and an opt-in classless base |
| 2 | * (plan L54 K1/K2). Load it after kit.css; a page that uses none of it loads neither this nor its bytes. |
| 3 | * |
| 4 | * The fluid scale (Utopia's idea): type steps and spaces that grow with the screen between 320 and 1240 px, by clamp() |
| 5 | * and with no breakpoints. Base 16 px at 320 and 18 px at 1240; ratio 1.2 then 1.25. |
| 6 | * --step--2 … --step-5 font sizes |
| 7 | * --space-3xs … --space-3xl gaps and padding (¼, ½, ¾, 1, 1½, 2, 3, 4 and 6 times the base) |
| 8 | * |
| 9 | * The primitives (Every Layout's idea): each a few lines, set by custom properties, and aware of their own width |
| 10 | * rather than the screen's, so they behave the same in a sidebar, a card or a whole page. |
| 11 | * .ov-stack children one under another, --gap apart |
| 12 | * .ov-cluster children in a wrapping row (tags, buttons, a toolbar); --gap, --justify, --align |
| 13 | * .ov-sidebar a side part --side wide beside the rest; it goes under once the rest would be under --min |
| 14 | * (the side part is the first child; .ov-sidebar--end when it is the last) |
| 15 | * .ov-switcher side by side until the box is narrower than --min, then all stacked: never two and one |
| 16 | * .ov-grid as many columns of at least --min as fit |
| 17 | * .ov-rail one row that scrolls sideways, items --item wide, snapping |
| 18 | * .ov-cover a box at least --min tall with one child (.is-centre) centred in it |
| 19 | * .ov-frame a box of --ratio that crops a picture or a video to fill it |
| 20 | * |
| 21 | * The classless base (Pico's idea): put .ov-base on <body> or <main> and plain HTML (headings, text, links, lists, |
| 22 | * quotes, code, tables, forms) looks finished on the site's theme, light or dark. Every selector is inside :where(), |
| 23 | * so it weighs nothing against a site's own rules. |
| 24 | */ |
| 25 | :root { |
| 26 | --step--2: clamp(0.6944rem, 0.6856rem + 0.0444vw, 0.72rem); |
| 27 | --step--1: clamp(0.8333rem, 0.8101rem + 0.1159vw, 0.9rem); |
| 28 | --step-0: clamp(1rem, 0.9565rem + 0.2174vw, 1.125rem); |
| 29 | --step-1: clamp(1.2rem, 1.1283rem + 0.3587vw, 1.4063rem); |
| 30 | --step-2: clamp(1.44rem, 1.3295rem + 0.5527vw, 1.7578rem); |
| 31 | --step-3: clamp(1.728rem, 1.5648rem + 0.8161vw, 2.1973rem); |
| 32 | --step-4: clamp(2.0736rem, 1.8395rem + 1.1704vw, 2.7466rem); |
| 33 | --step-5: clamp(2.4883rem, 2.1597rem + 1.6433vw, 3.4332rem); |
| 34 | --space-3xs: clamp(0.25rem, 0.2391rem + 0.0543vw, 0.2813rem); |
| 35 | --space-2xs: clamp(0.5rem, 0.4783rem + 0.1087vw, 0.5625rem); |
| 36 | --space-xs: clamp(0.75rem, 0.7174rem + 0.163vw, 0.8438rem); |
| 37 | --space-s: clamp(1rem, 0.9565rem + 0.2174vw, 1.125rem); |
| 38 | --space-m: clamp(1.5rem, 1.4348rem + 0.3261vw, 1.6875rem); |
| 39 | --space-l: clamp(2rem, 1.913rem + 0.4348vw, 2.25rem); |
| 40 | --space-xl: clamp(3rem, 2.8696rem + 0.6522vw, 3.375rem); |
| 41 | --space-2xl: clamp(4rem, 3.8261rem + 0.8696vw, 4.5rem); |
| 42 | --space-3xl: clamp(6rem, 5.7391rem + 1.3043vw, 6.75rem); |
| 43 | } |
| 44 | |
| 45 | /* ── Primitives ── */ |
| 46 | .ov-stack { display: flex; flex-direction: column; justify-content: flex-start; gap: var(--gap, var(--space-s)); } |
| 47 | .ov-stack > * { margin-block: 0; } |
| 48 | .ov-cluster { display: flex; flex-wrap: wrap; gap: var(--gap, var(--space-xs)); justify-content: var(--justify, flex-start); align-items: var(--align, center); } |
| 49 | .ov-sidebar { display: flex; flex-wrap: wrap; gap: var(--gap, var(--space-m)); align-items: var(--align, flex-start); } |
| 50 | .ov-sidebar > :first-child, .ov-sidebar--end > :last-child { flex-basis: var(--side, 18rem); flex-grow: 1; } |
| 51 | .ov-sidebar > :last-child, .ov-sidebar--end > :first-child { flex-basis: 0; flex-grow: 999; min-inline-size: var(--min, 55%); } |
| 52 | .ov-sidebar--end > :first-child { order: 0; } |
| 53 | .ov-switcher { display: flex; flex-wrap: wrap; gap: var(--gap, var(--space-s)); } |
| 54 | .ov-switcher > * { flex-grow: 1; flex-basis: calc((var(--min, 32rem) - 100%) * 999); min-inline-size: 0; } |
| 55 | .ov-grid { display: grid; gap: var(--gap, var(--space-s)); grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--min, 16rem)), 1fr)); } |
| 56 | .ov-rail { display: grid; grid-auto-flow: column; grid-auto-columns: var(--item, min(80%, 18rem)); gap: var(--gap, var(--space-s)); overflow-x: auto; overscroll-behavior-x: contain; |
| 57 | scroll-snap-type: x mandatory; scroll-padding-inline: var(--pad, 0px); padding-inline: var(--pad, 0px); padding-block-end: var(--space-2xs); scrollbar-width: thin; } |
| 58 | .ov-rail > * { scroll-snap-align: start; min-inline-size: 0; } |
| 59 | .ov-cover { display: flex; flex-direction: column; min-block-size: var(--min, 100svh); padding: var(--pad, var(--space-m)); box-sizing: border-box; } |
| 60 | .ov-cover > * { margin-block: var(--gap, var(--space-s)); } |
| 61 | .ov-cover > :first-child:not(.is-centre) { margin-block-start: 0; } |
| 62 | .ov-cover > :last-child:not(.is-centre) { margin-block-end: 0; } |
| 63 | .ov-cover > .is-centre { margin-block: auto; } |
| 64 | .ov-frame { aspect-ratio: var(--ratio, 16 / 9); overflow: hidden; display: flex; justify-content: center; align-items: center; max-inline-size: 100%; } |
| 65 | .ov-frame > :is(img, video, iframe, canvas, svg) { inline-size: 100%; block-size: 100%; object-fit: cover; } |
| 66 | |
| 67 | /* ── The classless base, under .ov-base ── */ |
| 68 | :where(.ov-base) { color: var(--ovk-text); font-family: var(--ovk-font); font-size: var(--step-0); line-height: 1.6; -webkit-text-size-adjust: 100%; } |
| 69 | :where(.ov-base) :where(h1, h2, h3, h4, h5, h6) { margin: 1.4em 0 .5em; line-height: 1.15; letter-spacing: -.015em; text-wrap: balance; color: var(--ovk-text); } |
| 70 | :where(.ov-base) :where(h1) { font-size: var(--step-5); letter-spacing: -.03em; margin-block-start: 0; } |
| 71 | :where(.ov-base) :where(h2) { font-size: var(--step-3); } |
| 72 | :where(.ov-base) :where(h3) { font-size: var(--step-2); } |
| 73 | :where(.ov-base) :where(h4) { font-size: var(--step-1); } |
| 74 | :where(.ov-base) :where(h5, h6) { font-size: var(--step-0); } |
| 75 | :where(.ov-base) :where(p, ul, ol, dl, blockquote, pre, table, figure, details) { margin: 0 0 1em; } |
| 76 | :where(.ov-base) :where(p, li, dd, blockquote) { max-inline-size: 68ch; } |
| 77 | :where(.ov-base) :where(small) { font-size: var(--step--1); color: var(--ovk-muted); } |
| 78 | :where(.ov-base) :where(a) { color: var(--ovk-accent); text-decoration-thickness: 1px; text-underline-offset: .18em; } |
| 79 | :where(.ov-base) :where(a:hover) { text-decoration-thickness: 2px; } |
| 80 | :where(.ov-base) :where(a, button, input, select, textarea, summary):focus-visible { outline: 2px solid var(--ovk-accent); outline-offset: 2px; } |
| 81 | :where(.ov-base) :where(ul, ol) { padding-inline-start: 1.3em; } |
| 82 | :where(.ov-base) :where(li + li) { margin-block-start: .3em; } |
| 83 | :where(.ov-base) :where(hr) { border: 0; border-block-start: 1px solid var(--ovk-line); margin: var(--space-l) 0; } |
| 84 | :where(.ov-base) :where(blockquote) { padding: .2em 0 .2em 1em; border-inline-start: 3px solid var(--ovk-accent); color: var(--ovk-muted); } |
| 85 | :where(.ov-base) :where(code, kbd, samp, pre) { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .88em; } |
| 86 | :where(.ov-base) :where(:not(pre) > code, kbd) { padding: .1em .35em; border-radius: 6px; background: var(--ovk-panel-2); } |
| 87 | :where(.ov-base) :where(pre) { padding: var(--space-xs) var(--space-s); overflow-x: auto; border: 1px solid var(--ovk-line); border-radius: var(--ovk-radius-sm); background: var(--ovk-panel); line-height: 1.5; } |
| 88 | :where(.ov-base) :where(img, video, svg, canvas, iframe) { max-inline-size: 100%; } |
| 89 | :where(.ov-base) :where(img, video) { block-size: auto; border-radius: var(--ovk-radius-sm); } |
| 90 | :where(.ov-base) :where(table) { inline-size: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; display: block; overflow-x: auto; } |
| 91 | :where(.ov-base) :where(th, td) { padding: .55em .7em; border-block-end: 1px solid var(--ovk-line); text-align: start; vertical-align: top; } |
| 92 | :where(.ov-base) :where(th) { font-size: var(--step--1); font-weight: 700; letter-spacing: .03em; color: var(--ovk-muted); } |
| 93 | :where(.ov-base) :where(details) { padding: .6em .9em; border: 1px solid var(--ovk-line); border-radius: var(--ovk-radius-sm); background: var(--ovk-panel); } |
| 94 | :where(.ov-base) :where(summary) { cursor: pointer; font-weight: 700; } |
| 95 | :where(.ov-base) :where(label) { display: block; margin-block-end: .3em; font-weight: 600; } |
| 96 | :where(.ov-base) :where(input:not([type=checkbox], [type=radio], [type=range], [type=color]), select, textarea) { |
| 97 | box-sizing: border-box; inline-size: 100%; min-block-size: var(--ovk-tap); padding: .5em .8em; border: 1px solid var(--ovk-line); border-radius: var(--ovk-radius-sm); |
| 98 | background: var(--ovk-input); color: var(--ovk-text); font: inherit; } |
| 99 | :where(.ov-base) :where(textarea) { min-block-size: 6em; resize: vertical; } |
| 100 | :where(.ov-base) :where(input:not([type=checkbox], [type=radio], [type=range], [type=color], [type=hidden]), select, textarea) { margin-block-end: 1em; } |
| 101 | :where(.ov-base) :where(input[type=range]) { accent-color: var(--ovk-fill); } |
| 102 | /* Radios and checkboxes, drawn (4.219.0; the owner, 11-12: "update the little dots … make sure all basic elements are |
| 103 | styled … custom with the theme system"): the site's fill when chosen, a ring that grows its dot, a tick that pops. */ |
| 104 | :where(.ov-base) :where(input[type=radio], input[type=checkbox]) { appearance: none; -webkit-appearance: none; display: inline-grid; place-items: center; |
| 105 | inline-size: 1.25em; block-size: 1.25em; margin: 0 .45em 0 0; vertical-align: -.25em; flex: none; |
| 106 | border: 2px solid color-mix(in srgb, var(--ovk-text) 32%, transparent); background: transparent; cursor: pointer; |
| 107 | transition: border-color var(--ov-dur-2, .2s) ease, background-color var(--ov-dur-2, .2s) ease; } |
| 108 | :where(.ov-base) :where(input[type=radio]) { border-radius: 50%; } |
| 109 | :where(.ov-base) :where(input[type=checkbox]) { border-radius: .35em; } |
| 110 | :where(.ov-base) :where(input[type=radio])::before { content: ""; inline-size: .5em; block-size: .5em; border-radius: 50%; background: var(--ovk-fill); |
| 111 | transform: scale(0); transition: transform var(--ov-dur-2, .2s) var(--ov-ease-out, cubic-bezier(.2, .9, .3, 1)); } |
| 112 | :where(.ov-base) :where(input[type=checkbox])::before { content: ""; inline-size: .72em; block-size: .72em; background: var(--ovk-on-fill); |
| 113 | clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); transform: scale(0); |
| 114 | transition: transform var(--ov-dur-2, .2s) var(--ov-ease-out, cubic-bezier(.2, .9, .3, 1)); } |
| 115 | :where(.ov-base) :where(input[type=radio], input[type=checkbox]):hover { border-color: color-mix(in srgb, var(--ovk-fill) 70%, transparent); } |
| 116 | :where(.ov-base) :where(input[type=radio]:checked) { border-color: var(--ovk-fill); } |
| 117 | :where(.ov-base) :where(input[type=checkbox]:checked) { border-color: var(--ovk-fill); background: var(--ovk-fill); } |
| 118 | :where(.ov-base) :where(input[type=radio]:checked, input[type=checkbox]:checked)::before { transform: scale(1); } |
| 119 | :where(.ov-base) :where(input[type=radio], input[type=checkbox]):focus-visible { outline: 2px solid var(--ovk-accent); outline-offset: 2px; } |
| 120 | :where(.ov-base) :where(label:has(> input[type=radio], > input[type=checkbox])) { display: flex; align-items: center; gap: .1em; font-weight: 500; cursor: pointer; } |
| 121 | :where(.ov-base) :where(input, textarea)::placeholder { color: var(--ovk-muted); opacity: .85; } |
| 122 | :where(.ov-base) :where(input:not([type=checkbox], [type=radio], [type=range], [type=color]), select, textarea):focus { outline: none; border-color: var(--ovk-accent); |
| 123 | box-shadow: 0 0 0 3px color-mix(in srgb, var(--ovk-accent) 24%, transparent); } |
| 124 | /* A plain details (no class of its own) gets a chevron that turns; a site's own details keeps its own. */ |
| 125 | :where(.ov-base) :where(details:not([class]) > summary:not([class])) { display: flex; align-items: center; justify-content: space-between; gap: .6em; list-style: none; } |
| 126 | :where(.ov-base) :where(details:not([class]) > summary:not([class]))::-webkit-details-marker { display: none; } |
| 127 | :where(.ov-base) :where(details:not([class]) > summary:not([class]))::after { content: ""; flex: none; inline-size: .5em; block-size: .5em; margin-inline-end: .2em; |
| 128 | border-inline-end: 2px solid currentColor; border-block-end: 2px solid currentColor; opacity: .6; transform: rotate(45deg); |
| 129 | transition: transform var(--ov-dur-2, .2s) var(--ov-ease-out, ease); } |
| 130 | :where(.ov-base) :where(details[open]:not([class]) > summary:not([class]))::after { transform: rotate(225deg); } |
| 131 | :where(.ov-base) :where(details[open]:not([class]) > summary:not([class])) { margin-block-end: .5em; } |
| 132 | :where(.ov-base) :where(button, input[type=submit], input[type=button]) { min-block-size: var(--ovk-tap); padding: .5em 1.1em; border: 0; border-radius: 999px; cursor: pointer; |
| 133 | background: var(--ovk-fill); color: var(--ovk-on-fill); font: inherit; font-weight: 700; } |
| 134 | :where(.ov-base) :where(button:disabled, input:disabled, select:disabled, textarea:disabled) { opacity: .55; cursor: not-allowed; } |
| 135 | :where(.ov-base) :where(fieldset) { margin: 0 0 1em; padding: var(--space-xs) var(--space-s); border: 1px solid var(--ovk-line); border-radius: var(--ovk-radius-sm); } |
| 136 | :where(.ov-base) :where(mark) { padding: 0 .2em; border-radius: 4px; background: color-mix(in srgb, var(--ovk-warn) 35%, transparent); color: inherit; } |
| 137 |