barbican-reset 3.32.0 → 3.33.0

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.
@@ -1,26 +1,32 @@
1
1
  <template>
2
- <br-card confirm done>
2
+ <br-card class="confirm-done">
3
3
  <done-icon />
4
- <br-card-title>Done</br-card-title>
5
- <br-card-text v-if="$slots.default">
4
+ <br-card-title class="title">Done</br-card-title>
5
+ <br-card-text v-if="$slots.default" class="margin-top-1">
6
6
  <slot />
7
7
  </br-card-text>
8
8
  </br-card>
9
9
  </template>
10
10
 
11
- <script>
11
+ <script setup>
12
+ import { onMounted } from 'vue'
13
+ import { animateDone } from '#scripts/animations/confirm'
14
+
12
15
  import BrCard from '#components/BrCard.vue'
13
16
  import BrCardTitle from '#components/BrCardTitle.vue'
14
17
  import BrCardText from '#components/BrCardText.vue'
15
-
16
- import Animations from '#animations/index'
17
18
  import DoneIcon from '#icons/confirm/done.vue'
18
19
 
19
- export default {
20
- mixins: [Animations],
21
- components: { BrCard, BrCardTitle, BrCardText, DoneIcon },
22
- mounted() {
23
- this.animateDone()
24
- },
25
- }
20
+ onMounted(() => animateDone())
26
21
  </script>
22
+
23
+ <style scoped>
24
+ .confirm-done {
25
+ max-width: var(--width-title);
26
+ text-align: center;
27
+ }
28
+
29
+ .title {
30
+ text-align: center;
31
+ }
32
+ </style>
@@ -1,33 +1,34 @@
1
1
  <template>
2
- <br-card confirm email>
2
+ <br-card class="confirm-email">
3
3
  <email-icon />
4
- <br-card-title v-if="$slots.title">
4
+ <br-card-title v-if="$slots.title" class="title">
5
5
  <slot name="title" />
6
6
  </br-card-title>
7
- <br-card-text v-if="$slots.default">
7
+ <br-card-text v-if="$slots.default" class="margin-top-1">
8
8
  <slot />
9
9
  </br-card-text>
10
10
  </br-card>
11
11
  </template>
12
12
 
13
- <script>
13
+ <script setup>
14
+ import { onMounted } from 'vue'
15
+ import { animateEmail } from '#scripts/animations/confirm'
16
+
14
17
  import BrCard from '#components/BrCard.vue'
15
18
  import BrCardTitle from '#components/BrCardTitle.vue'
16
19
  import BrCardText from '#components/BrCardText.vue'
17
-
18
- import Animations from '#animations/index'
19
20
  import EmailIcon from '#icons/confirm/email.vue'
20
21
 
21
- export default {
22
- mixins: [Animations],
23
- components: {
24
- BrCard,
25
- BrCardTitle,
26
- BrCardText,
27
- EmailIcon,
28
- },
29
- mounted() {
30
- this.animateEmail()
31
- },
32
- }
22
+ onMounted(() => animateEmail())
33
23
  </script>
24
+
25
+ <style scoped>
26
+ .confirm-email {
27
+ max-width: var(--width-title);
28
+ text-align: center;
29
+ }
30
+
31
+ .title {
32
+ text-align: center;
33
+ }
34
+ </style>
@@ -1,14 +1,22 @@
1
+ <!--
2
+ @component BrFormCheckbox
3
+ @description A checkbox form field that wraps BrFormInput (type="checkbox") inside a BrFormLabel.
4
+ Supports success/error states, disabled state, block-level layout, and an optional label slot.
5
+ Non-prop attributes (e.g. name, value) are forwarded directly to the underlying input element.
6
+ Any classes passed to this component are merged onto the root wrapper div.
7
+ -->
1
8
  <template>
2
- <div :class="['br-form-checkbox', { block }]">
9
+ <div :class="['br-form-checkbox', { block }, $attrs.class]">
3
10
  <br-form-label
4
11
  :class="[{ success }, { error }]"
5
12
  :disabled="disabled"
6
- :id="generateID">
13
+ :id="generateID"
14
+ :table="table">
7
15
  <br-form-input
8
16
  :class="[{ success }, { error }]"
9
17
  :disabled="disabled"
18
+ v-bind="inputAttrs"
10
19
  :id="generateID"
11
- v-bind="$attrs"
12
20
  type="checkbox"
13
21
  :error="error" />
14
22
  <span v-if="$slots.default" class="label-text">
@@ -19,21 +27,47 @@
19
27
  </template>
20
28
 
21
29
  <script setup>
22
- import { computed } from 'vue'
30
+ import { computed, useAttrs } from 'vue'
23
31
  import formatKebabCase from '#helpers/formatKebabCase'
24
32
  import BrFormLabel from '#components/BrFormLabel.vue'
25
33
  import BrFormInput from '#components/BrFormInput.vue'
26
34
 
35
+ /** All fallthrough attributes from the parent. */
36
+ const attrs = useAttrs()
37
+
38
+ /**
39
+ * Attrs forwarded to the inner input, with `class` removed so it doesn't
40
+ * double-apply (class is already merged onto the root div via $attrs.class).
41
+ *
42
+ * @type {import('vue').ComputedRef<Record<string, unknown>>}
43
+ */
44
+ const inputAttrs = computed(() => {
45
+ const { class: _, ...rest } = attrs
46
+ return rest
47
+ })
48
+
49
+ /**
50
+ * Disable automatic attribute inheritance so $attrs can be manually
51
+ * forwarded to the inner input element rather than the root div.
52
+ */
27
53
  defineOptions({
28
54
  inheritAttrs: false,
29
55
  })
30
56
 
57
+ /**
58
+ * @prop {string} [id] - Explicit id for the input/label pair. Falls back to a random id.
59
+ * @prop {boolean} [disabled] - Disables the checkbox input and label.
60
+ * @prop {boolean} [success] - Applies success styling to the label and input.
61
+ * @prop {boolean} [error] - Applies error styling to the label and input.
62
+ * @prop {boolean} [block] - Makes the checkbox wrapper display as a block-level element.
63
+ */
31
64
  const props = defineProps({
32
65
  id: String,
33
66
  disabled: Boolean,
34
67
  success: Boolean,
35
68
  error: Boolean,
36
69
  block: Boolean,
70
+ table: Boolean,
37
71
  })
38
72
 
39
73
  // @description Returns a string for the "id" attribute
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <component
3
- :class="['br-form-label', { disabled }]"
3
+ :class="['br-form-label', { disabled }, { table }]"
4
4
  :is="!disabled ? 'label' : 'div'"
5
5
  :for="!disabled ? id : null">
6
6
  <slot />
@@ -14,6 +14,7 @@ const props = defineProps({
14
14
  disabled: Boolean,
15
15
  required: Boolean,
16
16
  optional: Boolean,
17
+ table: Boolean,
17
18
  id: String,
18
19
  })
19
20
  </script>
@@ -10,24 +10,31 @@
10
10
  </template>
11
11
 
12
12
  <script setup>
13
- import { onMounted, onUnmounted } from 'vue';
14
- import BrButton from "#components/BrButton.vue";
15
- import CloseIcon from "#icons/close.vue";
13
+ import { onMounted, onUnmounted } from 'vue'
14
+ import { constrainTabbing } from '#scripts/helpers'
15
+ import BrButton from '#components/BrButton.vue'
16
+ import CloseIcon from '#icons/close.vue'
16
17
 
17
- const emit = defineEmits(["closeOverlay"]);
18
+ const emit = defineEmits(['closeOverlay'])
19
+
20
+ function constrainOverlayPadding() {
21
+ let $overlay = document.querySelector('.br-overlay-wrap')
22
+ constrainTabbing($overlay)
23
+ }
18
24
 
19
25
  function listenForKeys({ key }) {
20
26
  if (key == 'Escape') {
21
- emit('closeOverlay')
27
+ emit('closeOverlay')
22
28
  }
23
29
  }
24
30
 
25
31
  onMounted(() => {
26
32
  document.addEventListener('keydown', listenForKeys)
33
+ document.addEventListener('keydown', constrainOverlayPadding)
27
34
  })
28
35
 
29
36
  onUnmounted(() => {
30
37
  document.removeEventListener('keydown', listenForKeys)
38
+ document.removeEventListener('keydown', constrainOverlayPadding)
31
39
  })
32
40
  </script>
33
-
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div :class="generateClassnames">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { computed } from 'vue'
9
+
10
+ const generateClassnames = computed(() => {
11
+ let classnames = ['br-table-cell']
12
+
13
+ if (props.width) {
14
+ classnames.push('width-' + props.width)
15
+ }
16
+
17
+ if (props.right) {
18
+ classnames.push('align-right')
19
+ }
20
+
21
+ if (props.center) {
22
+ classnames.push('align-center')
23
+ }
24
+
25
+ return classnames
26
+ })
27
+
28
+ const props = defineProps({
29
+ width: {
30
+ type: String,
31
+ default: 'stretch',
32
+ },
33
+ right: Boolean,
34
+ center: Boolean,
35
+ })
36
+ </script>
@@ -1,9 +1,5 @@
1
1
  <template>
2
2
  <div class="br-table-header">
3
- <div class="br-table-header-title">
4
- <slot />
5
- </div>
6
- <div class="br-table-header-title yes">Yes</div>
7
- <div class="br-table-header-title no">No</div>
3
+ <slot />
8
4
  </div>
9
5
  </template>
@@ -0,0 +1,19 @@
1
+ <template>
2
+ <div :class="generateClassnames">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { computed } from 'vue'
9
+
10
+ const generateClassnames = computed(() => {
11
+ let classnames = ['br-table-row']
12
+ return classnames
13
+ })
14
+
15
+ const props = defineProps({
16
+ right: Boolean,
17
+ center: Boolean,
18
+ })
19
+ </script>
package/index.js CHANGED
@@ -45,7 +45,9 @@ import BrOverlay from '#components/BrOverlay.vue'
45
45
  import BrLoader from '#components/BrLoader.vue'
46
46
  import BrSkiplink from '#components/BrSkiplink.vue'
47
47
  import BrStatusBars from '#components/BrStatusBars.vue'
48
+ import BrTableCell from '#components/BrTableCell.vue'
48
49
  import BrTableHeader from '#components/BrTableHeader.vue'
50
+ import BrTableRow from '#components/BrTableRow.vue'
49
51
  import BrWrap from '#components/BrWrap.vue'
50
52
 
51
53
  export {
@@ -89,6 +91,8 @@ export {
89
91
  BrOverlay,
90
92
  BrSkiplink,
91
93
  BrStatusBars,
94
+ BrTableCell,
92
95
  BrTableHeader,
96
+ BrTableRow,
93
97
  BrWrap,
94
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barbican-reset",
3
- "version": "3.32.0",
3
+ "version": "3.33.0",
4
4
  "description": "Shared design system for Barbican projects, providing SCSS utilities, animations, icons, Vue components, and JS helpers for consistent styling and behaviour.",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -0,0 +1,79 @@
1
+ import gsap from 'gsap'
2
+
3
+ let defaults = {
4
+ duration: 0.6,
5
+ ease: 'power1.in',
6
+ transformOrigin: 'center',
7
+ }
8
+
9
+ let fadeIn = {
10
+ duration: 0.3,
11
+ opacity: 0,
12
+ delay: 0.3,
13
+ }
14
+
15
+ let explode = {
16
+ ease: 'power1.out',
17
+ opacity: 0,
18
+ scale: 1.5,
19
+ }
20
+
21
+ export function animateDone() {
22
+ let $card = document.querySelector('.br-card.confirm-done')
23
+ let $title = $card.querySelector('.br-card-title')
24
+ let $outline = $card.querySelector('.outline')
25
+ let $arrow = $card.querySelector('.arrow')
26
+ let $tick = $card.querySelector('.tick')
27
+
28
+ let tl = gsap.timeline({ defaults })
29
+
30
+ let $clone = $title.cloneNode(true)
31
+
32
+ $clone.classList.add('clone')
33
+
34
+ $title.after($clone)
35
+
36
+ let cloneHeight = $card.querySelector('.clone').offsetHeight
37
+
38
+ $clone.style.marginTop = `-${cloneHeight}px`
39
+
40
+ // prettier-ignore
41
+ tl.set($arrow, { opacity: 0 })
42
+ .from($title, fadeIn)
43
+ .set($clone, { opacity: 0.4 })
44
+ .from($outline, { drawSVG: '0%' }, 'start')
45
+ .from($tick, { drawSVG: '0%', duration: 0.3 }, 'start')
46
+ .to($clone, explode, 'start')
47
+ .set($arrow, { opacity: 1 })
48
+ .from($arrow, { x: -6, ease: 'power1.out' })
49
+ }
50
+
51
+ export function animateEmail() {
52
+ let $card = document.querySelector('.br-card.confirm-email')
53
+ let $title = $card.querySelector('.br-card-title')
54
+ let $outline = $card.querySelector('.outline')
55
+ let $arrow = $card.querySelector('.arrow')
56
+ let $fold = $card.querySelector('.fold')
57
+
58
+ let tl = gsap.timeline({ defaults })
59
+
60
+ let $clone = $title.cloneNode(true)
61
+
62
+ $clone.classList.add('clone')
63
+
64
+ $title.after($clone)
65
+
66
+ let cloneHeight = $card.querySelector('.clone').offsetHeight
67
+
68
+ $clone.style.marginTop = `-${cloneHeight}px`
69
+
70
+ // prettier-ignore
71
+ tl.set($arrow, { opacity: 0 })
72
+ .from($title, fadeIn)
73
+ .set($clone, { opacity: 0.4 })
74
+ .from($outline, { drawSVG: '0%' }, 'start')
75
+ .from($fold, { drawSVG: '0%', duration: 0.3 }, 'start')
76
+ .to($clone, explode, 'start')
77
+ .set($arrow, { opacity: 1 })
78
+ .from($arrow, { x: -6, ease: 'power1.out' })
79
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Constrains keyboard tab focus to within a given DOM element, creating a focus trap.
3
+ * When the user tabs past the last focusable element, focus wraps to the first,
4
+ * and vice versa when shift-tabbing past the first.
5
+ *
6
+ * Useful for modal dialogs and overlays where focus should not escape to the
7
+ * rest of the page while the element is active.
8
+ *
9
+ * @param {HTMLElement} element - The container element to trap focus within.
10
+ * @returns {void}
11
+ *
12
+ * @example
13
+ * constrainTabbing(document.getElementById('modal'))
14
+ */
15
+ export default function (element) {
16
+ let focusableEls = element.querySelectorAll('a[href]:not([disabled], [rel=noopener]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])')
17
+ let firstFocusableEl = focusableEls[0]
18
+ let lastFocusableEl = focusableEls[focusableEls.length - 1]
19
+ let KEYCODE_TAB = 9
20
+
21
+ element.addEventListener('keydown', function (event) {
22
+ let { key, keyCode, shiftKey } = event
23
+
24
+ var isTabPressed = key === 'Tab' || keyCode === KEYCODE_TAB
25
+
26
+ if (!isTabPressed) return
27
+
28
+ if (shiftKey) {
29
+ if (document.activeElement === firstFocusableEl) {
30
+ lastFocusableEl.focus()
31
+ event.preventDefault()
32
+ }
33
+ } else {
34
+ if (document.activeElement === lastFocusableEl) {
35
+ firstFocusableEl.focus()
36
+ event.preventDefault()
37
+ }
38
+ }
39
+ })
40
+ }
@@ -1,17 +1,10 @@
1
1
  /**
2
- * Logs an entire object to the console, including deeply nested children.
3
- * Uses `util.inspect` to avoid truncation of complex or deeply nested structures.
2
+ * Logs a value to the console as pretty-printed JSON.
4
3
  *
5
- * @param {*} target - The value to inspect and log.
4
+ * @param {*} target - The value to log.
6
5
  * @returns {void}
7
- *
8
- * @example
9
- * logObject({ complicated: 'object', with: { child: 'values' } })
10
6
  */
11
7
 
12
- import util from 'util'
13
-
14
8
  export default function (target) {
15
- let options = { showHidden: false, depth: null, colors: true }
16
- console.log(util.inspect(target, options))
9
+ return console.log(JSON.stringify(target, null, 2))
17
10
  }
@@ -1,5 +1,6 @@
1
1
  import compareArrays from '#helpers/compareArrays'
2
+ import constrainTabbing from '#helpers/constrainTabbing'
2
3
  import formatKebabCase from '#helpers/formatKebabCase'
3
4
  import logObject from '#helpers/logObject'
4
5
 
5
- export { compareArrays, formatKebabCase, logObject }
6
+ export { compareArrays, constrainTabbing, formatKebabCase, logObject }
package/scss/_atomic.scss CHANGED
@@ -9,6 +9,7 @@
9
9
  @use "atomic/min-widths";
10
10
  @use "atomic/paddings";
11
11
  @use "atomic/text-aligns";
12
+ @use "atomic/widths";
12
13
 
13
14
  .border-radius-lg {
14
15
  border-radius: var(--border-radius-lg);
@@ -48,4 +49,8 @@
48
49
 
49
50
  .display-inline-block {
50
51
  display: inline-block;
52
+ }
53
+
54
+ .word-wrap-break-word {
55
+ word-wrap: break-word;
51
56
  }
@@ -23,14 +23,14 @@ fieldset,
23
23
 
24
24
  @include small-up {
25
25
  .fieldset.table {
26
- grid-template-columns: auto calc(var(--width-column) * 2);
27
26
  margin-top: var(--margin-sm);
28
27
  border-radius: 0;
29
- display: grid;
28
+ display: flex;
30
29
  padding: 0;
31
30
  }
32
31
 
33
32
  .fieldset .wrap-legend {
33
+ width: stretch;
34
34
  margin-top: 0;
35
35
  }
36
36
 
@@ -0,0 +1,58 @@
1
+ @use "mixins/breakpoints" as *;
2
+ @use "mixins/br-wrap" as *;
3
+
4
+ @include medium-up {
5
+ .br-table-cell:not(.activeReturn) {
6
+ padding: var(--padding-md);
7
+ }
8
+
9
+ .br-table-cell:not(.width-stretch) {
10
+ flex-shrink: 0;
11
+ }
12
+
13
+ .br-table-cell:not(:first-child) {
14
+ border-left-color: var(--color-black-25-lighten);
15
+ border-left-width: var(--border-width-sm);
16
+ border-left-style: solid;
17
+ }
18
+
19
+ .br-table-cell.align-right {
20
+ text-align: right;
21
+ }
22
+
23
+ .br-table-cell.align-center {
24
+ text-align: center;
25
+ }
26
+
27
+ .br-table-cell label {
28
+ display: none;
29
+ }
30
+
31
+ .br-table-cell.cluster {
32
+ @include br-wrap--cluster;
33
+ }
34
+
35
+ .br-table-cell.cluster>* {
36
+ vertical-align: middle;
37
+ display: inline-block;
38
+ }
39
+ }
40
+
41
+ @include medium-down {
42
+ .br-table-cell:not(.activeReturn) {
43
+ @include br-wrap--cluster;
44
+
45
+ & {
46
+ width: initial;
47
+ }
48
+ }
49
+
50
+ .br-table-cell:not(.activeReturn):not(:last-child) {
51
+ margin-bottom: var(--margin-sm);
52
+ }
53
+
54
+ .br-table-cell:not(.activeReturn)>* {
55
+ vertical-align: middle;
56
+ display: inline-block;
57
+ }
58
+ }
@@ -3,58 +3,12 @@
3
3
  .br-table-header {
4
4
  border-color: var(--color-black-25-lighten);
5
5
  border-width: var(--border-width-sm);
6
- margin-bottom: var(--margin-sm);
6
+ margin-bottom: var(--margin-md);
7
7
  border-style: solid;
8
8
  display: none;
9
9
 
10
- @include small-up {
10
+ @include medium-up {
11
11
  background-color: var(--color-black-5-lighten);
12
- grid-template-columns: auto repeat(2, 5rem);
13
- display: grid;
12
+ display: flex;
14
13
  }
15
- }
16
-
17
- .br-table-header-title {
18
- padding: var(--padding-md);
19
- font-weight: bold;
20
- }
21
-
22
- .br-table-header-title+.br-table-header-title {
23
- border-left-color: var(--color-black-25-lighten);
24
- border-left-width: var(--border-width-sm);
25
- border-left-style: solid;
26
- }
27
-
28
- .br-table-header-title.yes,
29
- .br-table-header-title.no {
30
- text-align: center;
31
- }
32
-
33
- .br-table-rows {
34
- list-style: none;
35
- padding: 0;
36
- margin: 0;
37
- }
38
-
39
- .br-table-row {
40
- border-color: var(--color-black-25-lighten);
41
- border-width: var(--border-width-sm);
42
- border-style: solid;
43
-
44
- @include small-up {
45
- grid-template-columns: auto repeat(2, 5rem);
46
- display: grid;
47
- }
48
- }
49
-
50
- .br-table-row div {
51
- border-color: var(--color-black-25-lighten);
52
- border-width: var(--border-width-sm);
53
- padding: var(--padding-md);
54
- }
55
-
56
- .br-table-row div+div {
57
- border-left-color: var(--color-black-25-lighten);
58
- border-left-width: var(--border-width-sm);
59
- border-left-style: solid;
60
14
  }
@@ -0,0 +1,24 @@
1
+ @use "mixins/breakpoints" as *;
2
+
3
+ .br-table-row {
4
+ border-color: var(--color-black-25-lighten);
5
+ border-width: var(--border-width-sm);
6
+ border-style: solid;
7
+ }
8
+
9
+ .br-table-row:not(:last-child) {
10
+ margin-bottom: var(--margin-md);
11
+ }
12
+
13
+ @include medium-up {
14
+ .br-table-row {
15
+ display: flex;
16
+ }
17
+ }
18
+
19
+ @include medium-down {
20
+ .br-table-row {
21
+ border-radius: var(--border-radius-lg);
22
+ padding: var(--padding-md);
23
+ }
24
+ }
@@ -432,8 +432,8 @@
432
432
  --width-icon: 10rem;
433
433
  // Title column width (`320px`).
434
434
  --width-title: 20rem;
435
- // Extra-small layout max-width (`384px`).
436
- --width-layout-xs: 24rem;
435
+ // Extra-small layout max-width (`400px`).
436
+ --width-layout-xs: 25rem;
437
437
  // Small layout max-width (`800px`).
438
438
  --width-layout-sm: 50rem;
439
439
  // Medium layout max-width (`960px`).
@@ -1,3 +1,5 @@
1
+ @use "../mixins/font-size" as *;
2
+
1
3
  $iterations: (
2
4
  "lg": var(--font-size-lg),
3
5
  "h1": var(--font-size-h1),
@@ -12,4 +14,8 @@ $iterations: (
12
14
  .font-size-#{$name} {
13
15
  font-size: #{$value};
14
16
  }
17
+ }
18
+
19
+ .font-size-subtitle {
20
+ @include font-size-subtitle;
15
21
  }
@@ -20,4 +20,16 @@ $iterations: (
20
20
  #{$margin}: #{$value}rem;
21
21
  }
22
22
  }
23
+ }
24
+
25
+ @each $name, $value in $iterations {
26
+ .margin-top-bottom-#{$name} {
27
+ margin-bottom: #{$value}rem;
28
+ margin-top: #{$value}rem;
29
+ }
30
+
31
+ .margin-left-right-#{$name} {
32
+ margin-right: #{$value}rem;
33
+ margin-left: #{$value}rem;
34
+ }
23
35
  }
@@ -0,0 +1,43 @@
1
+ $iterations: (
2
+ "0": 0,
3
+ "0-5": 0.5,
4
+ "1": 1,
5
+ "1-5": 1.5,
6
+ "2": 2,
7
+ "2-5": 2.5,
8
+ "3": 3,
9
+ "3-5": 3.5,
10
+ "4": 4,
11
+ "4-5": 4.5,
12
+ "5": 5,
13
+ "5-5": 5.5,
14
+ "6": 6,
15
+ "6-5": 6.5,
16
+ "7": 7,
17
+ "7-5": 7.5,
18
+ "8": 8,
19
+ "8-5": 8.5,
20
+ "9": 9,
21
+ "9-5": 9.5,
22
+ "10": 10,
23
+ "10-5": 10.5,
24
+ "11": 11,
25
+ "11-5": 11.5,
26
+ "12": 12,
27
+ "12-5": 12.5,
28
+ "13": 13,
29
+ "13-5": 13.5,
30
+ "14": 14,
31
+ "14-5": 14.5,
32
+ "15": 15,
33
+ );
34
+
35
+ @each $name, $value in $iterations {
36
+ .width-#{$name} {
37
+ width: #{$value}rem;
38
+ }
39
+ }
40
+
41
+ .width-stretch {
42
+ width: stretch;
43
+ }
package/scss/index.scss CHANGED
@@ -21,7 +21,9 @@
21
21
  @use "br-promo";
22
22
  @use "br-select";
23
23
  @use "br-skiplink";
24
+ @use "br-table-cell";
24
25
  @use "br-table-header";
26
+ @use "br-table-row";
25
27
  @use "br-wrap";
26
28
 
27
29
  @use "city-of-london";
@@ -1,4 +1,5 @@
1
1
  @use "../mixins/breakpoints" as *;
2
+ @use "../mixins/font-size" as *;
2
3
  @use "card/default" as *;
3
4
  @use "card/inline" as *;
4
5
  @use "card/membership" as *;
@@ -62,11 +63,7 @@
62
63
  }
63
64
 
64
65
  @mixin br-card-subtitle {
65
- font-size: var(--font-size-h5);
66
-
67
- @include medium-up {
68
- font-size: var(--font-size-h4);
69
- }
66
+ @include font-size-subtitle;
70
67
  }
71
68
 
72
69
  @mixin br-card--body {
@@ -114,6 +111,6 @@
114
111
 
115
112
  @mixin br-card-text {
116
113
  +.br-card-text {
117
- margin-top: var(--margin-md);
114
+ margin-top: var(--margin-sm);
118
115
  }
119
116
  }
@@ -34,8 +34,7 @@
34
34
 
35
35
  @mixin br-wrap--preferences {
36
36
  @include small-up {
37
- grid-template-columns: var(--width-column) var(--width-column);
38
- display: grid;
37
+ display: flex;
39
38
  }
40
39
 
41
40
  @include small-down {
@@ -0,0 +1,9 @@
1
+ @use "../mixins/breakpoints" as *;
2
+
3
+ @mixin font-size-subtitle {
4
+ font-size: var(--font-size-h5);
5
+
6
+ @include medium-up {
7
+ font-size: var(--font-size-h4);
8
+ }
9
+ }
@@ -1,4 +1,5 @@
1
1
  @use "../../../mixins/buttons/outline" as *;
2
+ @use "../../../mixins/buttons/slim" as *;
2
3
 
3
4
  $iterations: (
4
5
  "member": var(--color-brand-membership),
@@ -12,6 +13,7 @@ $iterations: (
12
13
 
13
14
  @mixin btn-renew-membership {
14
15
  @include outline-button($color: white, $background: transparent);
16
+ @include btn-slim;
15
17
 
16
18
  &:focus,
17
19
  &:hover {
@@ -18,6 +18,7 @@
18
18
  @forward "core";
19
19
  @forward "festival";
20
20
  @forward "focus";
21
+ @forward "font-size";
21
22
  @forward "headings";
22
23
  @forward "input";
23
24
  @forward "loading";
@@ -1,3 +1,5 @@
1
+ @use "../../mixins/breakpoints" as *;
2
+
1
3
  @mixin br-form-checkbox--input-colors {
2
4
  &:not(.error):not(.success) {
3
5
  outline-color: var(--color-status-neutral);
@@ -79,6 +81,17 @@
79
81
  }
80
82
  }
81
83
 
84
+ &.table {
85
+ border-bottom-right-radius: 0;
86
+ border-bottom-left-radius: 0;
87
+ border-width: 0;
88
+
89
+ @include medium-up {
90
+ border-top-right-radius: 0;
91
+ border-top-left-radius: 0;
92
+ }
93
+ }
94
+
82
95
  &:not(.disabled) {
83
96
  cursor: pointer;
84
97
 
@@ -1,61 +0,0 @@
1
- import gsap from 'gsap'
2
-
3
- /** @type {gsap.TweenVars} Fade-in vars: animates element from transparent with a short delay */
4
- const fadeIn = {
5
- duration: 0.3,
6
- opacity: 0,
7
- delay: 0.3,
8
- }
9
-
10
- /** @type {gsap.TweenVars} Explode vars: scales element up and fades it out */
11
- const explode = {
12
- ease: 'power1.out',
13
- opacity: 0,
14
- scale: 1.5,
15
- }
16
-
17
- /** @type {gsap.TimelineVars} Default timeline settings shared across all confirm animations */
18
- const defaults = {
19
- duration: 0.6,
20
- ease: 'power1.in',
21
- transformOrigin: 'center',
22
- }
23
-
24
- /**
25
- * Animates the email confirmation card.
26
- * Fades in the title, draws the envelope outline and fold SVG paths,
27
- * explodes a cloned title, then slides in the arrow.
28
- */
29
- export const animateEmail = () => {
30
- const query = (target) => document.querySelector(`.card[email] ${target}`)
31
- const title = query('.card-title')
32
- const outline = query('.outline')
33
- const arrow = query('.arrow')
34
- const fold = query('.fold')
35
- const tl = gsap.timeline({ defaults })
36
-
37
- const clone = title.cloneNode(true)
38
- clone.classList.add('clone')
39
- title.after(clone)
40
-
41
- tl.set(arrow, { opacity: 0 }).from(title, fadeIn).set(clone, { opacity: 0.4 }).from(outline, { drawSVG: '0%' }, 'start').from(fold, { drawSVG: '0%', duration: 0.3 }, 'start').to(clone, explode, 'start').set(arrow, { opacity: 1 }).from(arrow, { x: -6, ease: 'power1.out' })
42
- }
43
-
44
- /**
45
- * Animates the done confirmation card.
46
- * Fades in the title, draws the circle outline and tick SVG paths,
47
- * and explodes a cloned title.
48
- */
49
- export const animateDone = () => {
50
- const query = (target) => document.querySelector(`.card[done] ${target}`)
51
- const title = query('.card-title')
52
- const outline = query('.outline')
53
- const tick = query('.tick')
54
- const tl = gsap.timeline({ defaults })
55
-
56
- const clone = title.cloneNode(true)
57
- clone.classList.add('clone')
58
- title.after(clone)
59
-
60
- tl.from(title, fadeIn).set(clone, { opacity: 0.4 }).from(outline, { drawSVG: '0%' }, 'start').from(tick, { drawSVG: '0%', duration: 0.3 }, 'start+=0.3').to(clone, explode, 'start')
61
- }
@@ -1,8 +0,0 @@
1
- import { animateEmail, animateDone } from '#animations/confirm'
2
-
3
- export default {
4
- methods: {
5
- animateEmail,
6
- animateDone,
7
- },
8
- }