From 070c167f7908f75a1afc56d24f88d22d04ad4f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Sch=C3=BCrch?= Date: Fri, 15 Nov 2024 11:24:00 +0100 Subject: [PATCH] feat(styles): add segmented-button styles --- packages/styles/index.html | 29 +- .../styles/src/elements/segmented-button.scss | 520 ++++++++++++------ 2 files changed, 372 insertions(+), 177 deletions(-) diff --git a/packages/styles/index.html b/packages/styles/index.html index 34eda24364..ce8f4a2672 100644 --- a/packages/styles/index.html +++ b/packages/styles/index.html @@ -33,7 +33,7 @@

Styles package playground

-
+ + +
+
+
+ + + + + +
+
diff --git a/packages/styles/src/elements/segmented-button.scss b/packages/styles/src/elements/segmented-button.scss index af4467e25a..38b2fe14ee 100644 --- a/packages/styles/src/elements/segmented-button.scss +++ b/packages/styles/src/elements/segmented-button.scss @@ -2,217 +2,385 @@ @use '../tokens/components'; @use '../mixins/utilities'; - tokens.$default-map: components.$post-segmented-button; -$element-height: tokens.get('button-segmented-elements-height'); -$border-width: tokens.get('button-segmented-border-width'); -$gap-inline: tokens.get('button-segmented-gap-inline'); -$enabled-bg: tokens.get('button-segmented-enabled-bg'); -$enabled-border: tokens.get('button-segmented-enabled-border'); -$horizontal-border-radius: tokens.get('button-segmented-horizontal-border-radius'); -$elevation: tokens.get('button-segmented-elevation'); -$selected-bg: tokens.get('button-segmented-selected-bg'); -$selected-border: tokens.get('button-segmented-selected-border'); -$padding-inline: tokens.get('button-segmented-padding-inline'); -$hover-border: tokens.get('button-segmented-hover-border'); -$hover-fg: tokens.get('button-segmented-hover-fg'); -$enabled-fg: tokens.get('button-segmented-enabled-fg'); -$font-weight: tokens.get('button-segmented-font-weight'); -$selected-fg: tokens.get('button-segmented-selected-fg'); -$vertical-border-radius: tokens.get('button-segmented-vertical-border-radius'); +$post-segmented-button-max-count: 8; .segmented-button-container { - container-type: inline-size; - container-name: segmented-container; - display: block; + container-name: segmented-container; + container-type: inline-size; } .segmented-button { - display: flex; - align-items: center; - justify-content: center; - background-color: transparent; - position: relative; - box-sizing: border-box; - transition: all 0.4s cubic-bezier(0.25, 1.4, 0.5, 0.9);; - border: $border-width solid $enabled-border; - box-shadow: $elevation; -} - -.segmented-button input { - position: absolute !important; - margin: 0 !important; - padding: 0 !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0); - width: 0 !important; - height: 0 !important; - border: 0 !important; - white-space: nowrap !important; -} + display: flex; + gap: tokens.get('button-segmented-gap-inline'); + align-items: stretch; + outline: tokens.get('button-segmented-border-width') solid + tokens.get('button-segmented-enabled-border'); + outline-offset: calc(tokens.get('button-segmented-border-width') * -1); + background-color: tokens.get('button-segmented-enabled-bg'); + border-radius: tokens.get('button-segmented-horizontal-border-radius'); + box-shadow: tokens.get('button-segmented-elevation'); -.segmented-button label { + label { + flex: 0 1 100%; display: flex; - width: 100%; - box-sizing: border-box; justify-content: center; align-items: center; - text-align: center; - cursor: pointer; - flex: 1 1 0px; position: relative; - padding-inline: $padding-inline; - height: $element-height; - transition: inherit; - border: $border-width solid $enabled-border; - @include utilities.focus-style(); -} + z-index: 2; + padding-inline: tokens.get('button-segmented-padding-inline'); + height: tokens.get('button-segmented-elements-height'); + border-radius: inherit; + cursor: pointer; + font-weight: tokens.get('button-segmented-font-weight'); + color: tokens.get('button-segmented-enabled-fg'); + text-align: center; + line-height: 1.2; + overflow-wrap: anywhere; -.segmented-button label:last-of-type::after { - content: ""; - display: block; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; - background-color: $selected-bg; - border-radius: $horizontal-border-radius; - opacity: 0; - transform: scaleX(0); - box-sizing: border-box; - transition: transform 0.4s cubic-bezier(0.25, 1.5, 0.15, 0.9); -} + input { + appearance: none !important; + user-select: none; + pointer-events: none; + position: absolute; + inset: 0; + margin: 0; + padding: 0; + border: tokens.get('button-segmented-border-width') solid transparent; + border-radius: inherit; + @include utilities.focus-style(); + } -.segmented-button input:checked + label { - color: $selected-fg; - z-index: 10; -} + &:last-of-type { + z-index: 1; + } -@container segmented-container (max-width: 501px) { - .segmented-button { - display: inline-flex; - z-index: 1; - flex-direction: column; - border-radius: $vertical-border-radius; - border: none; + &:not(:last-of-type) { + &:hover { + z-index: 3; + } } - .segmented-button input:checked + label:last-of-type::after { - opacity: 1; - transform: scaleY(1); - box-sizing: border-box; - border: $border-width solid $selected-border; + &:hover { + color: tokens.get('button-segmented-hover-fg'); + + input { + border-color: tokens.get('button-segmented-hover-border'); + } } - .segmented-button label { - flex: 0 0 auto; - margin-bottom: calc(-1 * $border-width); - z-index: 1; - border: $border-width solid $enabled-border; + &:has(input:checked) { + color: tokens.get('button-segmented-selected-fg'); + cursor: default; - &:hover { - z-index: 2; - border-color: $hover-border; - color: $hover-fg; + ~ :last-of-type, + &:last-of-type { + &::after { + display: block; + content: ''; + position: absolute; + inset: 0; + z-index: -1; + background-color: tokens.get('button-segmented-selected-bg'); + border: tokens.get('button-segmented-border-width') solid + tokens.get('button-segmented-selected-border'); + border-radius: inherit; + transition: transform 0.4s cubic-bezier(0.25, 1.4, 0.5, 0.9); } - } - - .segmented-button label:last-of-type::after { - height: calc($element-height - $border-width); - border-radius: inherit; - opacity: 0; - transition: border-radius 0.3s ease, opacity 0.3s ease; - transition: transform 0.4s cubic-bezier(0.25, 1.5, 0.5, 1); - } - - @mixin segmented-button-style($count) { - @for $i from 1 through $count { - .segmented-button-#{$count} input:nth-last-of-type(#{$i + 1}):checked ~ label:last-of-type::after { - transform: translateY(calc(#{$i * -100%})) scaleY(1); - opacity: 1; - - @if $i == ($count - 1) { - border-top-left-radius: calc($vertical-border-radius - $border-width); - border-top-right-radius: calc($vertical-border-radius - $border-width); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } @else { - border-radius: 0; - } - } + } + + &:hover { + input { + border-color: tokens.get('button-segmented-selected-border'); } } - - .segmented-button-container { - @include segmented-button-style(2); - @include segmented-button-style(3); - @include segmented-button-style(4); - @include segmented-button-style(5); - @include segmented-button-style(6); - @include segmented-button-style(7); - @include segmented-button-style(8); - @include segmented-button-style(9); - @include segmented-button-style(10); - } - - .segmented-button label:first-of-type { - border-top-left-radius: $vertical-border-radius; - border-top-right-radius: $vertical-border-radius; } - .segmented-button label:last-of-type { - border-bottom-left-radius: $vertical-border-radius; - border-bottom-right-radius: $vertical-border-radius; + @for $i from 1 through $post-segmented-button-max-count { + &:nth-last-of-type(#{$i + 1}):has(input:checked) ~ label:last-of-type::after { + transform: translateX(calc($i * -100% - $i * tokens.get('button-segmented-gap-inline'))); + } } + } } -@container segmented-container (min-width: 501px) { - .segmented-button { - gap: $gap-inline; - height: 100%; - height: $element-height; - border-radius: $horizontal-border-radius; - } +@container segmented-container (max-width: 600px) { + .segmented-button { + flex-direction: column; + border-radius: tokens.get('button-segmented-vertical-border-radius'); - .segmented-button input:checked + label:last-of-type::after { - opacity: 1; - transform: scaleX(1); - } + label { + flex: 1 0 auto; + border-radius: 0; - @for $i from 1 through 9 { - .segmented-button input:nth-last-of-type(#{$i + 1}):checked ~ label:last-of-type::after { - transform: translateX(calc(#{$i * -100%} - #{$i} * #{$border-width} * 1.55 - #{$i} * #{$gap-inline})) scaleX(1); - opacity: 1; + &:first-of-type { + border-top-left-radius: inherit; + border-top-right-radius: inherit; + + &:has(input:checked) ~ :last-of-type::after { + border-top-left-radius: tokens.get('button-segmented-vertical-border-radius'); + border-top-right-radius: tokens.get('button-segmented-vertical-border-radius'); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } - } - .segmented-button label { - display: flex; - align-items: center; - justify-content: center; - height: $element-height; - box-sizing: border-box; - border-radius: $horizontal-border-radius; - border: $border-width solid transparent; - z-index: 2; - - &:hover { - border-color: $hover-border; - color: $hover-fg; + } + + &:last-of-type { + border-bottom-left-radius: inherit; + border-bottom-right-radius: inherit; + + &::after { + border-radius: 0; + } + + &:has(input:checked)::after { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: tokens.get('button-segmented-vertical-border-radius'); + border-bottom-right-radius: tokens.get('button-segmented-vertical-border-radius'); + } + } + + &:not(:first-of-type, :last-of-type):has(input:checked) ~ :last-of-type::after { + border-radius: 0; + } + + &:not(:first-of-type) { + input { + border-top-color: tokens.get('button-segmented-enabled-border'); } + } - &:first-of-type { - margin-left: calc(-1 * $border-width); + &:not(:first-of-type) { + input { + top: calc(tokens.get('button-segmented-border-width') * -1); } + } - &:last-of-type { - margin-right: calc(-1 * $border-width); + &:hover { + input { + border-color: tokens.get('button-segmented-hover-border'); + } + } - &::after { - width: 100%; - } + @for $i from 1 through $post-segmented-button-max-count { + &:nth-last-of-type(#{$i + 1}):has(input:checked) ~ label:last-of-type::after { + transform: translateY(calc($i * -100% - $i * tokens.get('button-segmented-gap-inline'))); } + } } -} \ No newline at end of file + } +} + +// $element-height: tokens.get('button-segmented-elements-height'); +// $border-width: tokens.get('button-segmented-border-width'); +// $gap-inline: tokens.get('button-segmented-gap-inline'); +// $enabled-bg: tokens.get('button-segmented-enabled-bg'); +// $enabled-border: tokens.get('button-segmented-enabled-border'); +// $horizontal-border-radius: tokens.get('button-segmented-horizontal-border-radius'); +// $elevation: tokens.get('button-segmented-elevation'); +// $selected-bg: tokens.get('button-segmented-selected-bg'); +// $selected-border: tokens.get('button-segmented-selected-border'); +// $padding-inline: tokens.get('button-segmented-padding-inline'); +// $hover-border: tokens.get('button-segmented-hover-border'); +// $hover-fg: tokens.get('button-segmented-hover-fg'); +// $enabled-fg: tokens.get('button-segmented-enabled-fg'); +// $font-weight: tokens.get('button-segmented-font-weight'); +// $selected-fg: tokens.get('button-segmented-selected-fg'); +// $vertical-border-radius: tokens.get('button-segmented-vertical-border-radius'); + +// .segmented-button-container { +// container-type: inline-size; +// container-name: segmented-container; +// display: block; +// } + +// .segmented-button { +// display: flex; +// align-items: center; +// justify-content: center; +// background-color: transparent; +// position: relative; +// box-sizing: border-box; +// transition: all 0.4s cubic-bezier(0.25, 1.4, 0.5, 0.9);; +// border: $border-width solid $enabled-border; +// box-shadow: $elevation; +// } + +// .segmented-button input { +// position: absolute !important; +// margin: 0 !important; +// padding: 0 !important; +// overflow: hidden !important; +// clip: rect(0, 0, 0, 0); +// width: 0 !important; +// height: 0 !important; +// border: 0 !important; +// white-space: nowrap !important; +// } + +// .segmented-button label { +// display: flex; +// width: 100%; +// box-sizing: border-box; +// justify-content: center; +// align-items: center; +// text-align: center; +// cursor: pointer; +// flex: 1 1 0px; +// position: relative; +// padding-inline: $padding-inline; +// height: $element-height; +// transition: inherit; +// border: $border-width solid $enabled-border; +// @include utilities.focus-style(); +// } + +// .segmented-button label:last-of-type::after { +// content: ""; +// display: block; +// position: absolute; +// top: 0; +// right: 0; +// bottom: 0; +// left: 0; +// z-index: -1; +// background-color: $selected-bg; +// border-radius: $horizontal-border-radius; +// opacity: 0; +// transform: scaleX(0); +// box-sizing: border-box; +// transition: transform 0.4s cubic-bezier(0.25, 1.5, 0.15, 0.9); +// } + +// .segmented-button input:checked + label { +// color: $selected-fg; +// z-index: 10; +// } + +// @container segmented-container (max-width: 501px) { +// .segmented-button { +// display: inline-flex; +// z-index: 1; +// flex-direction: column; +// border-radius: $vertical-border-radius; +// border: none; +// } + +// .segmented-button input:checked + label:last-of-type::after { +// opacity: 1; +// transform: scaleY(1); +// box-sizing: border-box; +// border: $border-width solid $selected-border; +// } + +// .segmented-button label { +// flex: 0 0 auto; +// margin-bottom: calc(-1 * $border-width); +// z-index: 1; +// border: $border-width solid $enabled-border; + +// &:hover { +// z-index: 2; +// border-color: $hover-border; +// color: $hover-fg; +// } +// } + +// .segmented-button label:last-of-type::after { +// height: calc($element-height - $border-width); +// border-radius: inherit; +// opacity: 0; +// transition: border-radius 0.3s ease, opacity 0.3s ease; +// transition: transform 0.4s cubic-bezier(0.25, 1.5, 0.5, 1); +// } + +// @mixin segmented-button-style($count) { +// @for $i from 1 through $count { +// .segmented-button-#{$count} input:nth-last-of-type(#{$i + 1}):checked ~ label:last-of-type::after { +// transform: translateY(calc(#{$i * -100%})) scaleY(1); +// opacity: 1; + +// @if $i == ($count - 1) { +// border-top-left-radius: calc($vertical-border-radius - $border-width); +// border-top-right-radius: calc($vertical-border-radius - $border-width); +// border-bottom-left-radius: 0; +// border-bottom-right-radius: 0; +// } @else { +// border-radius: 0; +// } +// } +// } +// } + +// .segmented-button-container { +// @include segmented-button-style(2); +// @include segmented-button-style(3); +// @include segmented-button-style(4); +// @include segmented-button-style(5); +// @include segmented-button-style(6); +// @include segmented-button-style(7); +// @include segmented-button-style(8); +// @include segmented-button-style(9); +// @include segmented-button-style(10); +// } + +// .segmented-button label:first-of-type { +// border-top-left-radius: $vertical-border-radius; +// border-top-right-radius: $vertical-border-radius; +// } + +// .segmented-button label:last-of-type { +// border-bottom-left-radius: $vertical-border-radius; +// border-bottom-right-radius: $vertical-border-radius; +// } +// } + +// @container segmented-container (min-width: 501px) { +// .segmented-button { +// gap: $gap-inline; +// height: 100%; +// height: $element-height; +// border-radius: $horizontal-border-radius; +// } + +// .segmented-button input:checked + label:last-of-type::after { +// opacity: 1; +// transform: scaleX(1); +// } + +// @for $i from 1 through 9 { +// .segmented-button input:nth-last-of-type(#{$i + 1}):checked ~ label:last-of-type::after { +// transform: translateX(calc(#{$i * -100%} - #{$i} * #{$border-width} * 1.55 - #{$i} * #{$gap-inline})) scaleX(1); +// opacity: 1; +// } +// } +// .segmented-button label { +// display: flex; +// align-items: center; +// justify-content: center; +// height: $element-height; +// box-sizing: border-box; +// border-radius: $horizontal-border-radius; +// border: $border-width solid transparent; +// z-index: 2; + +// &:hover { +// border-color: $hover-border; +// color: $hover-fg; +// } + +// &:first-of-type { +// margin-left: calc(-1 * $border-width); +// } + +// &:last-of-type { +// margin-right: calc(-1 * $border-width); + +// &::after { +// width: 100%; +// } +// } +// } +// }