@webority/theme 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.scss ADDED
@@ -0,0 +1,15 @@
1
+ // Default-brand full chain — compiled to dist/webority-theme.css (the RCL static
2
+ // asset + the zero-toolchain convenience build). Consumers who want brand-exact
3
+ // output import the partials individually with their OWN _variables before bootstrap.
4
+ //
5
+ // Load order is LOAD-BEARING — never reorder:
6
+ // variables (before bootstrap) -> bootstrap -> tokens -> aliases -> components -> wui-components
7
+ // Flags are NOT imported here: _wui-components already imports them (the phone control needs
8
+ // them), and legacy @import does not dedupe, so a second import emitted all ~245 flag classes
9
+ // twice (≈490 selectors) and bloated the bundle.
10
+ @import "scss/variables";
11
+ @import "bootstrap/scss/bootstrap";
12
+ @import "scss/tokens";
13
+ @import "scss/wui-aliases";
14
+ @import "scss/components";
15
+ @import "scss/wui-components";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -13,6 +13,7 @@
13
13
  "directory": "packages/theme"
14
14
  },
15
15
  "files": [
16
+ "index.scss",
16
17
  "scss",
17
18
  "dist"
18
19
  ],
@@ -121,6 +121,69 @@
121
121
  --bs-btn-disabled-border-color: var(--wui-danger-strong);
122
122
  }
123
123
 
124
+ // Outline variants — tokenized like the solid buttons above so they follow a product's
125
+ // RUNTIME --wui-* brand instead of Bootstrap's compile-time colours. Resting = brand outline
126
+ // on a transparent fill; hover fills with the brand and flips the label to the on-brand text
127
+ // token; active deepens to the pressed fill.
128
+ .btn-outline-primary {
129
+ --bs-btn-color: var(--color-primary);
130
+ --bs-btn-border-color: var(--color-primary);
131
+ --bs-btn-hover-color: var(--wui-primary-text);
132
+ --bs-btn-hover-bg: var(--color-primary);
133
+ --bs-btn-hover-border-color: var(--color-primary);
134
+ --bs-btn-active-color: var(--wui-primary-text);
135
+ --bs-btn-active-bg: var(--wui-primary-pressed);
136
+ --bs-btn-active-border-color: var(--wui-primary-pressed);
137
+ --bs-btn-disabled-color: var(--color-primary);
138
+ --bs-btn-disabled-border-color: var(--color-primary);
139
+ }
140
+
141
+ // Outline danger uses the deepened --wui-danger-strong (5.80:1, carries white) that solid
142
+ // .btn-danger uses, so an outline-danger hover reads identically to solid danger.
143
+ .btn-outline-danger {
144
+ --bs-btn-color: var(--wui-danger-strong);
145
+ --bs-btn-border-color: var(--wui-danger-strong);
146
+ --bs-btn-hover-color: #fff;
147
+ --bs-btn-hover-bg: var(--wui-danger-strong);
148
+ --bs-btn-hover-border-color: var(--wui-danger-strong);
149
+ --bs-btn-active-color: #fff;
150
+ --bs-btn-active-bg: var(--wui-danger-strong-hover);
151
+ --bs-btn-active-border-color: var(--wui-danger-strong-hover);
152
+ --bs-btn-disabled-color: var(--wui-danger-strong);
153
+ --bs-btn-disabled-border-color: var(--wui-danger-strong);
154
+ }
155
+
156
+ // Soft neutral tinted button — a quieter alternative to .btn-light for a secondary
157
+ // action that shouldn't compete with a primary/danger CTA in the same row.
158
+ .btn-subtle {
159
+ --bs-btn-bg: var(--color-bg-muted);
160
+ --bs-btn-color: var(--color-text);
161
+ --bs-btn-border-color: transparent;
162
+ --bs-btn-hover-bg: var(--color-border);
163
+ --bs-btn-hover-color: var(--color-text);
164
+ --bs-btn-hover-border-color: transparent;
165
+ --bs-btn-active-bg: var(--color-border-strong);
166
+ --bs-btn-active-color: var(--color-text);
167
+ --bs-btn-active-border-color: transparent;
168
+ }
169
+
170
+ // Light outline for a dark banner/hero CTA, where .btn-outline-primary's brand-coloured
171
+ // border/text would disappear against a dark background. White is used directly (not a
172
+ // token) because it's the one colour that reads on any dark surface, brand-independent.
173
+ .btn-outline-light {
174
+ --bs-btn-color: #fff;
175
+ --bs-btn-bg: transparent;
176
+ --bs-btn-border-color: rgba(255, 255, 255, 0.6);
177
+ --bs-btn-hover-color: #fff;
178
+ --bs-btn-hover-bg: rgba(255, 255, 255, 0.15);
179
+ --bs-btn-hover-border-color: #fff;
180
+ --bs-btn-active-color: #fff;
181
+ --bs-btn-active-bg: rgba(255, 255, 255, 0.25);
182
+ --bs-btn-active-border-color: #fff;
183
+ --bs-btn-disabled-color: rgba(255, 255, 255, 0.5);
184
+ --bs-btn-disabled-border-color: rgba(255, 255, 255, 0.3);
185
+ }
186
+
124
187
  // Themed disabled state (fleet default). Bootstrap's own disabled look is just the
125
188
  // variant's colour at --bs-btn-disabled-opacity (.65) — barely different from the active
126
189
  // button on a tinted brand palette. Give the ordinary case a real desaturated/muted look
@@ -148,6 +211,9 @@
148
211
  // sm/lg are re-asserted so they keep their own rung instead of the base value.
149
212
  .btn {
150
213
  --bs-btn-border-radius: var(--wui-radius);
214
+ // Progressive enhancement only (Chrome/Safari recent). Unsupported engines
215
+ // keep a plain border-radius arc — not a cross-browser design contract.
216
+ corner-shape: squircle;
151
217
  }
152
218
  .btn-sm,
153
219
  .btn-group-sm > .btn {
@@ -164,6 +230,10 @@
164
230
  // padding, an icon–label gap, a rounded hover highlight tinted with the brand,
165
231
  // and a danger variant so a row-action menu reads as one considered control.
166
232
  .dropdown-menu {
233
+ // Elevated surface (like .card), not Bootstrap's default page --bs-body-bg: otherwise the
234
+ // menu blends into a tinted page and, in dark mode, resolves to the page colour instead of
235
+ // the elevated surface.
236
+ --bs-dropdown-bg: var(--color-surface);
167
237
  --bs-dropdown-min-width: 12rem;
168
238
  --bs-dropdown-padding-x: 0.375rem;
169
239
  --bs-dropdown-padding-y: 0.375rem;
@@ -232,7 +302,7 @@
232
302
  //
233
303
  // Reference values:
234
304
  // - height : 48px (h-12 = 3rem)
235
- // - radius : 16px (matches `.btn-squircle` literal)
305
+ // - radius : 16px (--wui-radius-xl)
236
306
  // - font : 14px / 500 (text-sm font-medium)
237
307
  // - padding-x : 20px (px-5) default — Watch demo / outline-dark
238
308
  // Primary CTA overrides to 24px (px-6) inline
@@ -247,7 +317,7 @@
247
317
  --bs-btn-font-size: 0.875rem; // 14px
248
318
  --bs-btn-font-weight: 500;
249
319
  --bs-btn-line-height: 1.25;
250
- --bs-btn-border-radius: 16px; // matches .btn-squircle exactly
320
+ --bs-btn-border-radius: var(--wui-radius-xl); // 16px
251
321
  min-height: 3rem; // 48px (h-12)
252
322
 
253
323
 
@@ -318,11 +388,10 @@
318
388
  }
319
389
 
320
390
 
321
- // ---- Card "lifted" (shadowless) -------------------------------------
322
- // Previously a soft drop-shadow card; now flat. The `.card-lifted`
323
- // class is preserved so existing call sites don't break — it now
324
- // expresses elevation only via the 1px hover translate (kept as a
325
- // tactile signal, not a depth cue). No box-shadow, here or on hover.
391
+ // ---- Card hover-lift (name is historical) -----------------------------
392
+ // `.card-lifted` does NOT cast a shadow — in-page cards stay flat. It only
393
+ // nudges 1px on hover (tactile signal). API prop stays `lifted` / class
394
+ // `card-lifted` so call sites don't break (TASK-06086).
326
395
  .card-lifted {
327
396
  transition: transform 0.2s var(--wui-ease-out-quart);
328
397
  &:hover {
@@ -358,50 +427,9 @@
358
427
 
359
428
 
360
429
  // ---- Inputs ---------------------------------------------------------
361
- // 5 locked states — default / hover / focus / disabled / error —
362
- // matching the reference `<Input>` component. Bootstrap drives the base
363
- // look via $input-* vars in _variables.scss; these blocks add the
364
- // hover-border-darken cue and the leading/trailing-slot wrapper.
365
- //
366
- // The error state is driven by `aria-invalid="true"` (NOT by Bootstrap's
367
- // .is-invalid class) so accessibility tooling and visual cue stay in
368
- // sync without extra markup.
369
-
370
- .form-control,
371
- .form-select {
372
- // Radius follows the token ladder (see component-radii note) rather than the
373
- // compiled $input-border-radius, so a product's --wui-radius override reaches
374
- // the raw Bootstrap inputs the way it already reaches the .input primitive.
375
- border-radius: var(--wui-radius);
376
- transition: border-color 0.15s ease;
377
- }
378
- .form-control:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]),
379
- .form-select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]) {
380
- border-color: var(--color-border-strong); // --wui-text-3 @ 60%
381
- }
382
- .form-control[aria-invalid="true"],
383
- .form-select[aria-invalid="true"] {
384
- border-color: var(--bs-danger);
385
- }
386
- .form-control[aria-invalid="true"]:focus,
387
- .form-select[aria-invalid="true"]:focus {
388
- border-color: var(--bs-danger);
389
- box-shadow: none;
390
- }
391
-
392
- // Optional helper class for forms that prefer the explicit Bootstrap
393
- // validation pattern. Mirrors aria-invalid styling.
394
- .form-control.is-invalid,
395
- .form-select.is-invalid {
396
- border-color: var(--bs-danger);
397
- background-image: none; // kill Bootstrap's exclamation-mark SVG
398
- padding-right: 0.875rem;
399
- }
400
- .form-control.is-invalid:focus,
401
- .form-select.is-invalid:focus {
402
- border-color: var(--bs-danger);
403
- box-shadow: none;
404
- }
430
+ // Field chrome (height, hover, focus, invalid) lives in wui/_controls.scss on
431
+ // `.form-control` / `.form-select` with `.input` as an alias — one skin, not two
432
+ // (TASK-06079). This partial only keeps the marketing-side input-group shell.
405
433
 
406
434
 
407
435
  // ---- Input group (leading / trailing slots) ------------------------
@@ -424,33 +452,32 @@
424
452
  align-items: center;
425
453
  gap: 0.5rem;
426
454
  padding-inline: 0.875rem;
427
- height: calc(2.5rem + 2px); // 40px box + 1px border each side
428
- background: var(--wui-bg-light, #FFFFFF);
429
- border: 1px solid var(--bs-border-color);
430
- border-radius: var(--radius); // matches the bare input radius
455
+ height: var(--wui-control-height);
456
+ background: var(--color-surface);
457
+ border: 1px solid var(--color-border);
458
+ border-radius: var(--radius);
431
459
  transition: border-color 0.15s ease;
432
-
433
-
434
460
  }
435
461
  .wui-input-group:hover:not(:focus-within):not(.is-disabled) {
436
- border-color: rgba(120, 130, 157, 0.6);
462
+ border-color: var(--color-border-strong);
437
463
  }
438
464
  .wui-input-group:focus-within {
439
- border-color: var(--wui-primary);
465
+ border-color: var(--color-primary);
440
466
  }
441
467
  .wui-input-group.is-disabled,
442
468
  .wui-input-group:has(input:disabled) {
443
- background: var(--wui-bg-muted);
469
+ background: var(--color-bg-muted);
444
470
  cursor: not-allowed;
445
- border-color: var(--bs-border-color);
471
+ border-color: var(--color-border);
446
472
  }
447
473
  .wui-input-group.has-error,
448
474
  .wui-input-group:has(input[aria-invalid="true"]) {
449
- border-color: var(--bs-danger);
475
+ border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
450
476
  }
451
477
 
452
478
  // Strip the bare input's own chrome — the wrapper carries it all.
453
- .wui-input-group > .form-control {
479
+ .wui-input-group > .form-control,
480
+ .wui-input-group > .input {
454
481
  flex: 1 1 auto;
455
482
  min-width: 0;
456
483
  height: 100%;
@@ -462,7 +489,9 @@
462
489
  border-radius: 0;
463
490
  }
464
491
  .wui-input-group > .form-control:focus,
465
- .wui-input-group > .form-control:hover {
492
+ .wui-input-group > .form-control:hover,
493
+ .wui-input-group > .input:focus,
494
+ .wui-input-group > .input:hover {
466
495
  border: 0;
467
496
  box-shadow: none !important;
468
497
  background: transparent;
@@ -546,16 +575,21 @@ button.wui-input-leading:focus-visible {
546
575
  }
547
576
 
548
577
 
549
- // ---- Squircle corners (iOS-style) -----------------------------------
550
- // Named opt-in utilities for a specific squircle radius without relying
551
- // on Bootstrap's $border-radius. A consuming product can layer a global
552
- // `corner-shape: squircle` rule on top (Chrome 138+ / Safari 16+; older
553
- // browsers fall back to the plain border-radius — no breakage).
554
- .btn-squircle {
555
- border-radius: 16px;
578
+ // ---- Radius step utilities ------------------------------------------
579
+ // Every .btn is already a squircle (see base .btn above), so a button that
580
+ // wants a larger corner only needs a bigger radius — hence .btn-radius-xl,
581
+ // named for what it does (an xl radius step) rather than the corner shape.
582
+ .btn-radius-xl {
583
+ --bs-btn-border-radius: var(--wui-radius-xl); // 16px
556
584
  }
585
+
586
+ // Generic (non-button) squircle rounding. Unlike .btn, an arbitrary element
587
+ // gets no corner-shape from the base rules, so this utility sets it itself —
588
+ // otherwise the name would lie and it'd render a plain 32px arc. Older
589
+ // browsers fall back to that arc (Chrome 138+ / Safari 16+ for the squircle).
557
590
  .rounded-squircle {
558
591
  border-radius: 32px;
592
+ corner-shape: squircle;
559
593
  }
560
594
 
561
595
 
@@ -737,39 +771,31 @@ button.wui-input-leading:focus-visible {
737
771
  }
738
772
 
739
773
 
740
- // ---- Refined native scrollbars --------------------------------------
741
- // Mirrors the locked design's always-visible scrollbar treatment:
742
- //
743
- // - 8px thin WebKit thumb (also Firefox via `scrollbar-width: thin`)
744
- // - Quiet grey-blue tint always visible at rest (~40% alpha)
745
- // - Brighter on hover (~60% alpha) and active (~80%)
746
- // - Pill thumb with a 2px transparent border (visual cushion)
747
- // - `scrollbar-gutter: stable` on <html>/<body> so the page never
748
- // shifts when scrollable content grows/shrinks. Internal containers
749
- // (avatars, badges, rounded buttons) intentionally skip the gutter
750
- // reservation — applying it everywhere would push centered glyphs
751
- // off-center inside rounded boxes.
774
+ // ---- Native scrollbars (document + opt-in) --------------------------
775
+ // Scoped to html/body and `.wui-scroll` — never `*` (TASK-06085). Global
776
+ // `*` restyled every nested scroller (modals, tiny panels) and fought
777
+ // component-local rails. AppScrollbar uses `.wui-scroll` (see wui/_scrollbar).
752
778
  //
753
- // `wwwroot/js/scroll-state.js` toggles `html.is-scrolling` for ~800ms
754
- // after each scroll event; the thumb gets an extra opacity bump while
755
- // the class is on, giving subtle "I'm scrolling now" feedback without
756
- // sacrificing the always-visible affordance.
779
+ // `scrollbar-gutter: stable` only on the document so internal boxes do not
780
+ // push centered glyphs off-center. `html.is-scrolling` (scroll-state.js)
781
+ // brightens the document thumb while the page is scrolling.
757
782
  html,
758
783
  body {
759
784
  scrollbar-gutter: stable;
760
- }
761
- * {
762
785
  scrollbar-width: thin;
763
786
  scrollbar-color: var(--wui-scrollbar) transparent;
764
787
  }
765
- *::-webkit-scrollbar {
788
+ html::-webkit-scrollbar,
789
+ body::-webkit-scrollbar {
766
790
  width: 8px;
767
791
  height: 8px;
768
792
  }
769
- *::-webkit-scrollbar-track {
793
+ html::-webkit-scrollbar-track,
794
+ body::-webkit-scrollbar-track {
770
795
  background: transparent;
771
796
  }
772
- *::-webkit-scrollbar-thumb {
797
+ html::-webkit-scrollbar-thumb,
798
+ body::-webkit-scrollbar-thumb {
773
799
  background-color: var(--wui-scrollbar);
774
800
  border-radius: 9999px;
775
801
  border: 2px solid transparent;
@@ -778,29 +804,31 @@ body {
778
804
  min-width: 36px;
779
805
  transition: background-color 220ms ease;
780
806
  }
781
- *::-webkit-scrollbar-thumb:hover {
807
+ html::-webkit-scrollbar-thumb:hover,
808
+ body::-webkit-scrollbar-thumb:hover {
782
809
  background-color: var(--wui-scrollbar-hover);
783
810
  background-clip: padding-box;
784
811
  }
785
- *::-webkit-scrollbar-thumb:active {
812
+ html::-webkit-scrollbar-thumb:active,
813
+ body::-webkit-scrollbar-thumb:active {
786
814
  background-color: var(--wui-scrollbar-active);
787
815
  background-clip: padding-box;
788
816
  }
789
- *::-webkit-scrollbar-corner {
817
+ html::-webkit-scrollbar-corner,
818
+ body::-webkit-scrollbar-corner {
790
819
  background: transparent;
791
820
  }
792
821
 
793
- // While the page is actively scrolling, give the thumb a slight extra
794
- // pop so the user can track its position more easily.
795
- html.is-scrolling * {
822
+ html.is-scrolling,
823
+ html.is-scrolling body {
796
824
  scrollbar-color: var(--wui-scrollbar-hover) transparent;
797
825
  }
798
- html.is-scrolling *::-webkit-scrollbar-thumb {
826
+ html.is-scrolling::-webkit-scrollbar-thumb,
827
+ html.is-scrolling body::-webkit-scrollbar-thumb {
799
828
  background-color: var(--wui-scrollbar-hover);
800
829
  }
801
830
 
802
- // Utility: completely hide a scrollbar (e.g., for marquee/horizontal
803
- // carousels where the rail itself is visually intrusive).
831
+ // Hide a scrollbar (marquee / horizontal carousels).
804
832
  .scrollbar-hide {
805
833
  scrollbar-width: none;
806
834
  }
package/scss/_tokens.scss CHANGED
@@ -51,20 +51,18 @@
51
51
  // Easing curves — match locked design's CSS / Framer Motion exports
52
52
  --wui-ease-out-quart: cubic-bezier(0.22, 1, 0.36, 1);
53
53
  --wui-ease-ui: cubic-bezier(0.32, 0.72, 0, 1);
54
-
55
- // Elevation — exposes the Bootstrap $box-shadow* SCSS vars (set in
56
- // _variables.scss) as CSS vars so inline styles / JS / the portal
57
- // can reach the same values Bootstrap utilities use.
58
- --wui-shadow-card: #{$box-shadow-sm};
59
- --wui-shadow-card-lifted: #{$box-shadow};
60
- --wui-shadow-card-lifted-hover: #{$box-shadow-lg};
61
-
62
- // Overlay elevation. The rule: in-page surfaces stay FLAT (a card is a
63
- // border, not a shadow); only things that float above the page lift —
64
- // menus, popovers, modals. One ink (the heading navy) so every overlay
65
- // casts the same light; components must never hand-roll a shadow.
66
- --wui-shadow-overlay: 0 8px 28px -6px rgba(7, 20, 55, 0.14);
67
- --wui-shadow-overlay-lg: 0 24px 60px -16px rgba(7, 20, 55, 0.28);
54
+ // Elastic / spring — overshoots the target then settles (the y > 1 control point).
55
+ // Used by the tab ink-bar so a tab switch springs into place rather than sliding flat.
56
+ --wui-ease-elastic: cubic-bezier(0.34, 1.56, 0.64, 1);
57
+
58
+ // Elevation (TASK-06080). In-page card tokens are flat (none). Overlay
59
+ // rungs match $wui-shadow-overlay* in _variables.scss — one ink for menus,
60
+ // popovers, modals. Do not hand-roll a third shadow.
61
+ --wui-shadow-card: none;
62
+ --wui-shadow-card-lifted: none;
63
+ --wui-shadow-card-lifted-hover: none;
64
+ --wui-shadow-overlay: #{$wui-shadow-overlay};
65
+ --wui-shadow-overlay-lg: #{$wui-shadow-overlay-lg};
68
66
 
69
67
  // One height for every form control. Buttons, inputs and selects share form
70
68
  // rows, so they must line up — they were 46.8px and 45px, which reads as a
@@ -108,10 +106,10 @@
108
106
  --wui-neutral-bg-light: #F9FBFC;
109
107
 
110
108
  // ---- Accent-dark -----------------------------------------------
111
- // Link hover, focused button, deep-purple CTA underline. Matches
112
- // $link-hover-color in _variables.scss but exposed as a CSS
113
- // var so JS / inline styles can reach it.
114
- --wui-accent-dark: #4338CA;
109
+ // Darker primary for pressed/hover accents in CSS (not SCSS $link-hover).
110
+ // Aliased onto --wui-primary-pressed so a product that rebrands the pressed
111
+ // token once also updates this name (was a frozen indigo hex).
112
+ --wui-accent-dark: var(--wui-primary-pressed);
115
113
 
116
114
  // ---- Container width + responsive padding ----------------------
117
115
  // The standalone design uses a 1512px max canvas with generous side
@@ -220,16 +218,27 @@
220
218
  --wui-font-display: var(--bs-body-font-family);
221
219
 
222
220
  // ---- Z-index scale -------------------------------------------------
223
- // Bootstrap's own ladder ($zindex-dropdown etc.), exposed as CSS vars so a component
224
- // or a consuming product can layer against the same scale instead of inventing values.
221
+ // One page-level ladder. Local stacking inside a component (1, 5, 10, 40…) is
222
+ // NOT on this scale — only fixed/portalled layers that compete across the page.
223
+ //
224
+ // Why not Bootstrap's raw 1040/1050 for modals? App shell sticky chrome uses
225
+ // 1040–1045; a BS-height modal would paint under the mobile drawer. Modals are
226
+ // elevated to 1500+ so the shell never covers a dialog. Portalled menus
227
+ // (select / date / phone / infotip) sit at 1600 so a combobox inside a modal
228
+ // is not trapped under the dialog. Never invent a third number in component CSS —
229
+ // pick a rung from this list (TASK-06078).
225
230
  --wui-z-dropdown: 1000;
226
231
  --wui-z-sticky: 1020;
227
232
  --wui-z-fixed: 1030;
228
- --wui-z-modal-backdrop: 1040;
229
- --wui-z-modal: 1050;
230
- --wui-z-popover: 1060;
231
- --wui-z-tooltip: 1070;
232
- --wui-z-toast: 1080;
233
+ --wui-z-shell: 1040; // splash, shell base chrome
234
+ --wui-z-shell-scrim: 1044; // mobile drawer scrim
235
+ --wui-z-shell-drawer: 1045; // mobile sidebar drawer
236
+ --wui-z-modal-backdrop: 1500;
237
+ --wui-z-modal: 1505;
238
+ --wui-z-menu: 1600; // portalled select/date/phone menus + infotip
239
+ --wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
240
+ --wui-z-tooltip: 1650;
241
+ --wui-z-toast: 1700;
233
242
  }
234
243
 
235
244
  // Bootstrap md / xl (not invented px)