diff --git a/.changeset/six-owls-cry.md b/.changeset/six-owls-cry.md new file mode 100644 index 0000000000..46f219c0a9 --- /dev/null +++ b/.changeset/six-owls-cry.md @@ -0,0 +1,6 @@ +--- +'@swisspost/design-system-documentation': minor +'@swisspost/design-system-styles': minor +--- + +Added the option for a Button animation to the left. diff --git a/packages/documentation/src/stories/components/button/button.stories.ts b/packages/documentation/src/stories/components/button/button.stories.ts index 4310f67252..21d03b6aa2 100644 --- a/packages/documentation/src/stories/components/button/button.stories.ts +++ b/packages/documentation/src/stories/components/button/button.stories.ts @@ -21,7 +21,7 @@ const meta: MetaComponent = { type: 'button', variant: 'btn-primary', size: 'null', - animated: true, + animated: 'btn-animated', icon: 'null', iconOnly: false, iconPosition: 'start', @@ -121,14 +121,20 @@ const meta: MetaComponent = { }, animated: { name: 'Animated', - description: 'When set to `true`, the component animates on hover.', + description: 'Sets an animation on hover.', if: { arg: 'icon', eq: 'null', }, control: { - type: 'boolean', + type: 'inline-radio', + labels: { + 'null': 'None', + 'btn-animated': 'End', + 'btn-animated-start': 'Start', + }, }, + options: ['null', 'btn-animated', 'btn-animated-start'], table: { category: 'General', }, @@ -215,7 +221,7 @@ type Story = StoryObj; const Template = { render: (args: Args) => { const tagName = unsafeStatic(args.tag); - const isAnimated = args.tag !== 'input' && args.animated; + const isAnimated = args.tag !== 'input' && args.animated !== 'none'; const props = createProps(args, isAnimated); if (args.tag === 'input') { @@ -244,7 +250,7 @@ function createProps(args: Args, isAnimated: boolean) { 'btn', args.variant, args.size, - isAnimated && 'btn-animated', + args.animated, args.iconOnly && 'btn-icon', ...additionalClasses, ] diff --git a/packages/styles/src/components/button.scss b/packages/styles/src/components/button.scss index 51896e4e49..8fe92f2145 100644 --- a/packages/styles/src/components/button.scss +++ b/packages/styles/src/components/button.scss @@ -9,7 +9,7 @@ @use './../variables/type'; @use './../variables/spacing'; @use './../mixins/color' as color-mx; -@use './../mixins/icons'; +@use './../mixins/icons' as icon-mx; @use './../mixins/utilities'; @use './../mixins/forms'; @use './../mixins/button' as button-mx; @@ -138,22 +138,48 @@ } // Animated + +.btn-animated, +.btn-animated-start { + &:not(.btn-link, .btn-tertiary) { + &::after { + @include icon-mx.icon(2111); + content: ''; + display: block; + height: 2em; + width: 2em; + position: absolute; + transition: + opacity 250ms, + transform 250ms; + opacity: 0; + } + + > span { + transition: transform 250ms; + } + + @media (prefers-reduced-motion: no-preference) { + @include utilities.not-disabled-focus-hover() { + &::after { + transform: translateX(0); + opacity: 1; + } + } + + > span { + // Initially transform to place text in the right rendering context for a smooth animation + transform: translateX(0); + } + } + } +} + .btn-animated:not(.btn-link, .btn-tertiary) { &::after { - content: ''; - position: absolute; - right: button.$btn-padding-x-md - button.$btn-animation-distance-md; - width: 0.625em; - height: 0.625em; - transform: rotateZ(315deg) - translate(button.$btn-border-width * -1, button.$btn-border-width * -1); - transform-origin: center center; - transition: - opacity 250ms, - transform 250ms; - border-right: button.$btn-border-width solid currentColor; - border-bottom: button.$btn-border-width solid currentColor; - opacity: 0; + right: button.$btn-padding-x-md - button.$btn-animation-distance-md - + (button.$btn-font-size-md * 2 / 3); + transform: translateX(button.$btn-border-width * -1); } @each $size in button.$btn-non-default-sizes { @@ -161,14 +187,11 @@ right: map.get(button.$btn-padding-x-map, $size) - map.get( button.$btn-animation-distance-map, $size - ); + ) - + (map.get(button.$btn-font-size-map, $size) * 2 / 3); } } - > span { - transition: transform 250ms; - } - // Only animate when user prefers to see animations @media (prefers-reduced-motion: no-preference) { @include utilities.not-disabled-focus-hover() { @@ -181,16 +204,40 @@ transform: translateX(map.get(button.$btn-animation-distance-map, $size) * -1); } } + } + } +} - &::after { - transform: rotateZ(315deg) translate(0, 0); - opacity: 1; - } +.btn-animated-start:not(.btn-link, .btn-tertiary) { + &::after { + @include icon-mx.icon(2110); + left: button.$btn-padding-x-md - button.$btn-animation-distance-md - + (button.$btn-font-size-md * 2 / 3); + transform: translateX(button.$btn-border-width); + } + + @each $size in button.$btn-non-default-sizes { + &.btn-#{$size}::after { + left: map.get(button.$btn-padding-x-map, $size) - map.get( + button.$btn-animation-distance-map, + $size + ) - + (map.get(button.$btn-font-size-map, $size) * 2 / 3); } + } - > span { - // Initially transform to place text in the right rendering context for a smooth animation - transform: translateX(0); + // Only animate when user prefers to see animations + @media (prefers-reduced-motion: no-preference) { + @include utilities.not-disabled-focus-hover() { + > span { + transform: translateX(map.get(button.$btn-animation-distance-map, md)); + } + + @each $size in button.$btn-non-default-sizes { + &.btn-#{$size} > span { + transform: translateX(map.get(button.$btn-animation-distance-map, $size)); + } + } } } }