Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add animations for segmented buttons #109

Merged
merged 2 commits into from
May 11, 2024

Conversation

not-nullptr
Copy link
Contributor

i'm fairly sure m3 doesn't animate these icons officially, but it is far too abrupt to ignore right now (at least in my opinion)

@KTibow
Copy link
Owner

KTibow commented May 10, 2024

FYI:

  • The way Material Catalog does it is fade through
  • In general, Material Web does this by using an animated check
  • I could add a slidethrough animation by deleting .custom-icon .icon { transition: none; }. I experimented with some variations, all seem pretty goofy.

I do somewhat like this animation, I might try to find a simpler animation but if not I'll probably merge a CSS-only version of this

@not-nullptr
Copy link
Contributor Author

sorry, i'm a fairly js-brained person. would be really nice if you could find a css-only way since the animation jumps atm if you interrupt it and doing it css-only would fix that

@KTibow
Copy link
Owner

KTibow commented May 10, 2024

Thoughts on this one?
Screencast from 2024-05-09 18-15-37.webm

@not-nullptr
Copy link
Contributor Author

i think i prefer my one, but i think yours would make more sense as a default. maybe it could be an option?

@KTibow
Copy link
Owner

KTibow commented May 10, 2024

It's not much but it's honest work
Screencast from 2024-05-09 18-43-15.webm

Note that this was made with transitions instead of keyframe, keyframe would allow for "storing"/"unstoring" the icons in different ways to be closer to your original (eg check is unstored with a 90deg turn while it's stored with a 45deg turn), but I'm not exactly sure how to make it not run on load

@not-nullptr
Copy link
Contributor Author

these look great!

@KTibow
Copy link
Owner

KTibow commented May 11, 2024

Here's the component's code

<script lang="ts">
  import type { HTMLLabelAttributes } from "svelte/elements";
  import Icon from "$lib/misc/_icon.svelte";
  import type { IconifyIcon } from "@iconify/types";
  import iconCheck from "@ktibow/iconset-material-symbols/check";

  export let display = "flex";
  export let extraOptions: HTMLLabelAttributes = {};
  export let input: string;
  export let icon: IconifyIcon | undefined = undefined;
</script>

<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<label
  for={input}
  class="m3-font-label-large"
  style="display: {display}; overflow: hidden;"
  {...extraOptions}
>
  <div class="layer" />
  <div class="check icon">
    <Icon icon={iconCheck} />
  </div>
  {#if icon}
    <div class="custom icon">
      <Icon {icon} />
    </div>
  {/if}
  <div class="start-pad pad" />
  <slot />
  {#if !icon}
    <div class="end-pad pad" />
  {/if}
</label>

<style>
  label {
    padding: 0 1rem;
    /* flex: 1; */
    min-width: 3rem;
    align-items: center;
    justify-content: center;

    --text: var(--m3-scheme-on-surface);
    color: rgb(var(--text));
    transition: all 200ms;

    cursor: pointer;
    white-space: nowrap;
    user-select: none;
    position: relative;
  }
  :global(label) ~ label {
    border-left: 0.0625rem solid rgb(var(--m3-scheme-outline));
  }
  :global(input:disabled) + label {
    color: rgb(var(--m3-scheme-on-surface) / 0.38);
    cursor: auto;
  }
  .layer {
    position: absolute;
    inset: 0;
    transition: all 200ms;
  }
  .icon {
    height: 1.125rem;
    transition: all 200ms;
    flex-shrink: 0;
    transform-origin: 0.563rem 0.563rem;
  }
  .icon > :global(svg) {
    width: 1.125rem;
    height: 1.125rem;
  }

  .check.icon {
    width: 0;
    opacity: 0;
    rotate: -60deg;
  }
  :global(input:checked) + label > .check.icon {
    opacity: 1;
    rotate: 0deg;
  }
  .custom.icon {
    width: 0;
    opacity: 0;
    rotate: 60deg;
  }
  :global(input:not(:checked)) + label > .custom.icon {
    opacity: 1;
    rotate: 0deg;
  }

  .pad {
    transition: all 200ms;
    flex-shrink: 0;
  }
  .start-pad {
    width: 0.8125rem;
  }
  .end-pad {
    width: 0.8125rem;
  }
  :global(input:checked) + label > .start-pad,
  .custom.icon + .start-pad {
    width: 1.625rem;
  }
  :global(input:checked) + label > .end-pad {
    width: 0rem;
  }

  label {
    -webkit-tap-highlight-color: transparent;
  }
  @media (hover: hover) {
    :global(input:not(:disabled)) + label:hover > .layer {
      background-color: rgb(var(--text) / 0.08);
    }
  }

  :global(input:checked) + label {
    background-color: rgb(var(--m3-scheme-secondary-container));
    --text: var(--m3-scheme-on-secondary-container);
  }
  :global(input:enabled:focus-visible) + label > .layer,
  :global(input:enabled) + label:active > .layer {
    background-color: rgb(var(--text) / 0.12);
  }
</style>

@not-nullptr
Copy link
Contributor Author

all looks good to me, other than the a11y ignore (which is necessary due to how you've done the component.) lgtm?

@KTibow
Copy link
Owner

KTibow commented May 11, 2024

Oh that was because I was working on the ripple branch. I'll update this branch to it soon, and hopefully will get this merged.

@not-nullptr
Copy link
Contributor Author

Oh that was because I was working on the ripple branch. I'll update this branch to it soon, and hopefully will get this merged.

word. could we get the ripple merged soon? i need it for a project :3

@KTibow KTibow merged commit dbcef79 into KTibow:main May 11, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants