shapcnGitHub

Frameworks

The examples that import shape-aware-morph use the npm package. It is in progress. See Install.

<morph-icon> is a custom element, so it works in any framework. Define the element once, then use the tag.

Plain HTML

Use the script tag. Set the name attribute, or the name property, or call a method.

<script type="module" src="https://shapcn.com/morph-icon.js"></script>
<button id="toggle" type="button" aria-label="Toggle microphone">
  <morph-icon name="mic" size="32" duration="420"></morph-icon>
</button>
<script type="module">
  const icon = document.querySelector('morph-icon');
  let on = true;
  document.getElementById('toggle').addEventListener('click', () => {
    on = !on;
    icon.name = on ? 'mic' : 'mic-off';   // same as setAttribute('name', ...)
  });
  await icon.morphTo('mic-off');          // methods return a Promise
</script>

React

Define the element once, then use the tag. Use string or number values for the attributes.

import { useEffect, useState } from 'react';
import { registerLucide, defineMorphIcon } from 'shape-aware-morph';

export function MicButton() {
  const [on, setOn] = useState(true);
  useEffect(() => {
    registerLucide().then(() => defineMorphIcon());
  }, []);
  return (
    <button onClick={() => setOn(!on)}>
      <morph-icon name={on ? 'mic' : 'mic-off'} duration="420" />
    </button>
  );
}

Call defineMorphIcon in an effect. On the server there is no DOM, and defineMorphIcon returns null.

React types

The package types add morph-icon to JSX.IntrinsicElements, to the react module, and to HTMLElementTagNameMap. Any import of shape-aware-morph brings them in. The attributes are typed as name, size, color, stroke-width, duration, easing, stagger, effect, and hover, plus class, className, style, id, key, and ref.

To call the methods, type the ref with MorphIconElement.

import { useRef } from 'react';
import type { MorphIconElement } from 'shape-aware-morph';

export function Hide() {
  const ref = useRef<MorphIconElement>(null);
  return (
    <>
      <morph-icon ref={ref} name="heart" />
      <button onClick={() => ref.current?.hide()}>Hide</button>
    </>
  );
}

Vue

Tell Vue that morph-icon is a custom element. Then use the tag.

// vite.config.ts
import vue from '@vitejs/plugin-vue';

export default {
  plugins: [
    vue({ template: { compilerOptions: { isCustomElement: (tag) => tag === 'morph-icon' } } }),
  ],
};
<script setup>
import { ref, onMounted } from 'vue';
import { registerLucide, defineMorphIcon } from 'shape-aware-morph';

const on = ref(true);
onMounted(async () => {
  await registerLucide();
  defineMorphIcon();
});
</script>

<template>
  <button @click="on = !on">
    <morph-icon :name="on ? 'mic' : 'mic-off'" duration="420" />
  </button>
</template>

Svelte

Svelte passes the tag through. Define the element when the component mounts.

<script>
  import { onMount } from 'svelte';
  import { registerLucide, defineMorphIcon } from 'shape-aware-morph';

  let on = true;
  onMount(async () => {
    await registerLucide();
    defineMorphIcon();
  });
</script>

<button on:click={() => (on = !on)}>
  <morph-icon name={on ? 'mic' : 'mic-off'} duration="420"></morph-icon>
</button>

Custom element rules

Markdown version