---
title: How to make myself rotate randomly every time i click with Astro (Translated by DeelL)
tags:  #cybozu #css #javascript #react #astro  
author: [Futa HIRAKOBA](https://www.docswell.com/user/korosuke613)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/5EGX38RWJL.jpg?width=480
description: IMPORTANT: THIS SLIDE HAS BEEN MACHINE TRANSLATED. original slide: https://www.docswell.com/s/korosuke613/ZLLQJQ-2023-06-30-rotate-my-icon  --- This slide was presented at an internal Cybozu Frontend Day event on Friday, June 30, 2023.  Click on my image on the top page of https://korosuke613.dev/ to rotate it randomly (X-axis, Y-axis, Z-axis, X &amp; Y-axis, X &amp; Z-axis, Y &amp; Z-axis).  We will teach you how to do it with Astro (MPA) using React.  I&#039;m not familiar with front-end technologies, so my presentation may not be correct in terminology, etc. Please understand.  [keywords]  CSS, JavaScript, React, Astro, Browser
published: June 30, 23
canonical: https://www.docswell.com/s/korosuke613/58GMW2-2023-06-30-rotate-my-icon-en
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/5EGX38RWJL.jpg)

How to make myself rotate
randomly every time I click with
Astro
(Translated by DeepL)
Jun 30, 2023 (Fri)
Cybozu Frontend Day
平木場 風太&gt;
Futa Hirakoba &lt;


# Page. 2

![Page Image](https://bcdn.docswell.com/page/4JQ859VQ7P.jpg)

Futa Hirakoba -
平木場 風太
🌋 Hometown - Kagoshima
🏢 Where I work - Cybozu K.K. / Development Division / Productivity Team
🧑‍💻 役割 - Engineering Productivity
🍣 Favorite Food - Chicken Nanban, spicy noodles .
💪 Often touches AWS, CI/CD tools, Terraform, etc.
💀 I don’t know anything about the front end.
[1]
@korosuke613
@shitimi_613
1. The photo shows Masumoto’s spicy noodles (tomato 5 spicy Chinese noodles topped with cheese). I want to eat it every day.
[ 2 / 46 ]


# Page. 3

![Page Image](https://bcdn.docswell.com/page/K7429XMYJ1.jpg)

https://korosuke613.dev
[ 3 / 46 ]


# Page. 4

![Page Image](https://bcdn.docswell.com/page/LJ1MKN8NEG.jpg)

korosuke613.dev | Z
軸の回転
共有
見る
https://korosuke613.dev
[ 4 / 46 ]


# Page. 5

![Page Image](https://bcdn.docswell.com/page/GJW3D4GMJ2.jpg)

https://korosuke613.dev
[ 5 / 46 ]


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4EZVG2LMJ3.jpg)

Click!
https://korosuke613.dev
[ 5 / 46 ]


# Page. 7

![Page Image](https://bcdn.docswell.com/page/Y765YVW5EV.jpg)

Rotation!
https://korosuke613.dev
[ 6 / 46 ]


# Page. 8

![Page Image](https://bcdn.docswell.com/page/G75YGDMG74.jpg)

Rotation!
https://korosuke613.dev
[ 7 / 46 ]


# Page. 9

![Page Image](https://bcdn.docswell.com/page/9J2GY89DJR.jpg)

I&#039;ll do the spinning
🙃.
[ 8 / 46 ]


# Page. 10

![Page Image](https://bcdn.docswell.com/page/DEYVG64MEM.jpg)

Furthermore...?
[ 9 / 46 ]


# Page. 11

![Page Image](https://bcdn.docswell.com/page/VJNMGZY378.jpg)

korosuke613.dev |
ランダムな軸の回転
共有
見る
https://korosuke613.dev
[ 10 / 46 ]


# Page. 12

![Page Image](https://bcdn.docswell.com/page/YE9NWZPPE3.jpg)

The direction of rotation is random.
😵‍💫
[ 11 / 46 ]


# Page. 13

![Page Image](https://bcdn.docswell.com/page/GE8KN1DX7D.jpg)

It changes with each click.
[ 12 / 46 ]


# Page. 14

![Page Image](https://bcdn.docswell.com/page/LELZPQMNJR.jpg)

https://www.geogebra.org/3d/czwmzrfr
[ 13 / 46 ]


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4JMV41YQJW.jpg)

X-axis
Y axis
Z axis
X-axis &amp; Y-axis
X-axis &amp; Z-axis
Y-axis &amp; Z-axis
[ 14 / 46 ]


# Page. 16

![Page Image](https://bcdn.docswell.com/page/PJR2WL9KE9.jpg)

korosuke613.dev
Hirakiba website
I created this as a place to organize my output, links to SNS, and other information.
Composed of Astro + React (second generation)
(aside) The first generation was configured with Nuxt + Vue
(Aside) Migration to Nuxt3 was too hard, so we had to rebuild with a newer framework + React, which
we use in our work.
What is Astro?
Astro is an all-in-one web framework for building fast, content-focused websites.
https://docs.astro.build/ja/getting-started/
より
Adopt MPA (Multi Page Application)
High speed because it is designed to reduce JS execution on the client as much as possible
Can use React as a UI framework
[ 15 / 46 ]


# Page. 17

![Page Image](https://bcdn.docswell.com/page/PEXDPWQ57X.jpg)

I&#039;ve got a website, but it&#039;s boring
😑.
[ 16 / 46 ]


# Page. 18

![Page Image](https://bcdn.docswell.com/page/3EKXDV9R7D.jpg)

🤩
Yes,
I
know.
Let&#039;s
spin
ourselves
around
.
[ 17 / 46 ]


# Page. 19

![Page Image](https://bcdn.docswell.com/page/L735RMWGE5.jpg)

How to rotate
Do your best with CSS
And while we’re at it, we’ll make it zoom in and out (`scale`).
Rotate only once, because infinite rotation is too much work.
Add the following CSS class to the icon element
Example: CSS for an animation that rotates around the Z axis
.rotate-animation-z {
animation: rotate-anime-z 1.5s linear 1 /* animation name time curve iteration count */
}
/* Define an animation that rotates around the Z-axis */
/* Specify between 0% and 100%, respectively */
/* rotate is the rotation angle, scale is the scaling */
@keyframes rotate-anime-z {
0% {transform: rotate(0deg) scale(1);}
25% {transform: rotate(90deg) scale(1.5);}
50% {transform: rotate(180deg) scale(1);}
75% {transform: rotate(270deg) scale(1.5);}
100% {transform: rotate(360deg) scale(1);}
}
[ 18 / 46 ]


# Page. 20

![Page Image](https://bcdn.docswell.com/page/87DDY6KN7G.jpg)

I want to rotate when I click.
As it is, the page ends up rotating one revolution when it loads.
It makes no sense to the viewer when it suddenly turns (and stops after one rotation).
Rotate when clicked.
Add CSS class on click (`click`), remove CSS class when animation ends (`animationend`,
`animationcancel`)
If you don’t remove the CSS class, it will only rotate on the first click.
Use React’s `useEffect` to register event listeners at load time
[ 19 / 46 ]


# Page. 21

![Page Image](https://bcdn.docswell.com/page/VJPX6YKN78.jpg)

I want to rotate when I click.
Browser
My icon
User
page loading
register event handler
const cssClassName = &#039;rotate-animation-z&#039;;
export const MyIcon = (props: IMyIconProps) =&gt; {
useEffect(() =&gt; {
const thisImg = document.getElementById(props.iconId);
if (thisImg === null) return;
// Give animation class on click
thisImg.addEventListener(&#039;click&#039;, () =&gt; {
thisImg.classList.add(cssClassName);
});
loop
click my icon
(click event)
add animation class
(handle click event)
Rotate
animation end
(animationend event)
remove animation class
(handle animationend event)
// Delete the animation class when the animation is finished
const removeClass = () =&gt; {
thisImg.classList.remove(cssClassName);
};
thisImg.addEventListener(&#039;animationend&#039;, removeClass);
thisImg.addEventListener(&#039;animationcancel&#039;, removeClass);
});
return (&lt;img /*
*/ /&gt;);
};
省略
Browser
My icon
User
[ 20 / 46 ]


# Page. 22

![Page Image](https://bcdn.docswell.com/page/2EVL3GVYJQ.jpg)

Rotated!
[ 21 / 46 ]


# Page. 23

![Page Image](https://bcdn.docswell.com/page/57GX38LWEL.jpg)

Something is missing
😕
[ 22 / 46 ]


# Page. 24

![Page Image](https://bcdn.docswell.com/page/4EQ859YQJP.jpg)

🤣
Let&#039;s
randomize
the
direction
of
rotation every time .
[ 23 / 46 ]


# Page. 25

![Page Image](https://bcdn.docswell.com/page/KJ429XWYE1.jpg)

I want to change the direction of rotation with
each click.
At the moment, there is only one
rotation on the Z-axis.
Randomly rotate X, Y, Z, XZ, XY,
and YZ axes
When deleting animations, it is not
known which animation class is
granted, so delete them all.
const cssClassNames = [
&#039;rotate-animation-x&#039;,
&#039;rotate-animation-y&#039;,
&#039;rotate-animation-z&#039;,
&#039;rotate-animation-xy&#039;,
&#039;rotate-animation-xz&#039;,
&#039;rotate-animation-yz&#039;,
];
export const MyIcon = (props: IMyIconProps) =&gt; {
useEffect(() =&gt; {
const thisImg = document.getElementById(props.iconId);
if (thisImg === null) return;
// Randomly assign animation class on click
thisImg.addEventListener(&#039;click&#039;, () =&gt; {
const randNum = Math.floor(Math.random() * cssClassNames.length);
const cssClassName = cssClassNames[randNum] || &#039;rotate-animation-z&#039;;
thisImg.classList.add(cssClassName);
});
// Delete the animation class when the animation is finished
const removeClass = () =&gt; {
cssClassNames.forEach((cssClassName) =&gt; {
// I don&#039;t know which classes are granted, so I delete them all.
thisImg.classList.remove(cssClassName);
});
};
thisImg.addEventListener(&#039;animationend&#039;, removeClass);
thisImg.addEventListener(&#039;animationcancel&#039;, removeClass);
});
[ 24 / 46 ]


# Page. 26

![Page Image](https://bcdn.docswell.com/page/LE1MKNYN7G.jpg)

X-axis
Y axis
Z axis
Rotated!
X-axis &amp; Y-axis
X-axis &amp; Z-axis
Y-axis &amp; Z-axis
[ 25 / 46 ]


# Page. 27

![Page Image](https://bcdn.docswell.com/page/GEW3D41ME2.jpg)

desktop
Actually, this website is responsive.
smart phone
[ 26 / 46 ]


# Page. 28

![Page Image](https://bcdn.docswell.com/page/47ZVG2PME3.jpg)

The display of the two icons is switched by screen
width
Because the icons are positioned differently on the desktop and on the phone
&lt;Section&gt;
&lt;div className=&quot;flex flex-col items-center md:flex-row md:justify-between md:gap-x-24&quot;&gt;
&lt;div&gt;
&lt;h1 className=&quot;hidden text-3xl font-bold md:block&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;flex flex-row justify-between md:hidden md:gap-x-24&quot;&gt;
{/* For mobile phone display */}
&lt;h1 className=&quot;text-3xl font-bold&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;h-20 w-20&quot; id=&quot;my-icon-small&quot;&gt;
&lt;MyIcon /&gt; {/* Small icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;p className=&quot;mt-6 text-xl leading-9&quot;&gt; {/* Self-introduction */} &lt;/p&gt;
&lt;div className=&quot;mt-3 flex flex-wrap gap-1&quot;&gt; {/* SNS Links */} &lt;/div&gt;
&lt;/div&gt;
&lt;div className=&quot;hidden shrink-0 md:block&quot;&gt;
&lt;div className=&quot;h-72 w-72&quot; id=&quot;my-icon-large&quot;&gt;
&lt;MyIcon /&gt; {/* Big icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
/Section&gt;
[ 27 / 46 ]


# Page. 29

![Page Image](https://bcdn.docswell.com/page/YJ65YVM57V.jpg)

The display of the two icons is switched by screen
width
Because the icons are positioned differently on the desktop and on the phone
&lt;Section&gt;
&lt;div className=&quot;flex flex-col items-center md:flex-row md:justify-between md:gap-x-24&quot;&gt;
&lt;div&gt;
&lt;h1 className=&quot;hidden text-3xl font-bold md:block&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;flex flex-row justify-between md:hidden md:gap-x-24&quot;&gt;
{/* For mobile phone display */}
&lt;h1 className=&quot;text-3xl font-bold&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;h-20 w-20&quot; id=&quot;my-icon-small&quot;&gt;
&lt;MyIcon /&gt; {/* Small icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;p className=&quot;mt-6 text-xl leading-9&quot;&gt; {/* Self-introduction */} &lt;/p&gt;
&lt;div className=&quot;mt-3 flex flex-wrap gap-1&quot;&gt; {/* SNS Links */} &lt;/div&gt;
&lt;/div&gt;
&lt;div className=&quot;hidden shrink-0 md:block&quot;&gt;
&lt;div className=&quot;h-72 w-72&quot; id=&quot;my-icon-large&quot;&gt;
&lt;MyIcon /&gt; {/* Big icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
/Section&gt;
[ 27 / 46 ]


# Page. 30

![Page Image](https://bcdn.docswell.com/page/GJ5YGDZGJ4.jpg)

Add animation to each icon
Get icon elements by class name
instead of Id
Assign a handler to each element
&lt;div className=&quot;h-20 w-20 my-icon&quot;&gt;
&lt;MyIcon /&gt; {/* Small icon */}
&lt;/div&gt;
&lt;div className=&quot;h-72 w-72 my-icon&quot;&gt;
&lt;MyIcon /&gt; {/* Big icon */}
&lt;/div&gt;
export const MyIcon = (props: IMyIconProps) =&gt; {
useEffect(() =&gt; {
const icons = document.getElementsByClassName(props.iconClass);
Array.from(icons).forEach((icon) =&gt; {
// Randomly assign animation class on click
icon.addEventListener(&#039;click&#039;, () =&gt; {
const randNum = Math.floor(Math.random() * cssClassNames.length);
const pickedCssClassName =
cssClassNames[randNum] || &#039;rotate-animation-z&#039;;
icon.classList.add(pickedCssClassName);
});
// Delete the animation class when the animation is finished
const removeClass = () =&gt; {
cssClassNames.forEach((cssClassName) =&gt; {
// I don&#039;t know which classes are granted, so I delete them all.
icon.classList.remove(cssClassName);
});
};
icon.addEventListener(&#039;animationend&#039;, removeClass);
icon.addEventListener(&#039;animationcancel&#039;, removeClass);
});
});
[ 28 / 46 ]


# Page. 31

![Page Image](https://bcdn.docswell.com/page/LE35RM9PJ5.jpg)

Multiple animation classes are assigned to a single element.
Therefore, the probability of the animation coming out is not uniform.
[ 29 / 46 ]


# Page. 32

![Page Image](https://bcdn.docswell.com/page/8EDDY6G3EG.jpg)

page load
`useEffect` of large icon
grant random
animation class
grant a random
animation class
small icon `useEffect`
Grant random
animation class
grant a random
animation class
element of large icon
small icon element
ex: &#039;rotate-animation-z&#039;, &#039;rotate-animation-xy&#039;
ex: &#039;rotate-animation-x&#039;, &#039;rotate-animation-y&#039;
Each of the `MyIcon` components is a
For elements with `.my-icon`,
. To randomly assign animation classes.
[ 30 / 46 ]


# Page. 33

![Page Image](https://bcdn.docswell.com/page/V7PX6Y3PE8.jpg)

Why not just handle one element in one component
→ There&#039;s a reason it&#039;s not easy...
🧐.
[ 31 / 46 ]


# Page. 34

![Page Image](https://bcdn.docswell.com/page/2JVL3G4V7Q.jpg)

How to call the current component
It calls `MyIcon` with the following structure
Astro (`index.astro`)
├── TSX (`SelfIntroduction.tsx`)
└── TSX (`MyIcon.tsx`)
`MyIcon` is called from Astro and passed as a small element to the `SelfIntroduction` component
The reason for not calling `MyIcon.tsx` from `SelfIntroduction.tsx` has to do with Astro properties
&lt;SelfIntroduction&gt;
&lt;MyIcon
client:idle
iconPath={/* PATH to icon image */}
iconClass=&quot;my-icon&quot;
/&gt;
&lt;/SelfIntroduction&gt;
[ 32 / 46 ]


# Page. 35

![Page Image](https://bcdn.docswell.com/page/5EGX3811JL.jpg)

Astro’s philosophy is to minimize JS on the client
Astro is a framework that does as much server-side rendering as possible, so it is designed to minimize JS
execution on the client [1].
Thus, to get JS to run on the client, you must declare a `client:*` directive
`client:idle`: load and hydrate the component’s JavaScript after the page has initially loaded and the
`requestIdleCallback` event has occurred [2].
Example (`index.astro`)
&lt;SelfIntroduction&gt;
&lt;MyIcon
client:idle {/* it won&#039;t work without it */}
iconPath={/* PATH to icon image */}
iconClass=&quot;my-icon&quot;
/&gt;
&lt;/SelfIntroduction&gt;
1. https://docs.astro.build/en/concepts/why-astro/#server-first
2. https://docs.astro.build/en/reference/directives-reference/#clientidle
[ 33 / 46 ]


# Page. 36

![Page Image](https://bcdn.docswell.com/page/4JQ859DN7P.jpg)

You have to apply client:* to MyIcon .
`MyIcon` uses gory JS and requires `client:*` configuration
`client:*` can only be called from `*.astro`.
The effect of `client:*` extends to child components of the given component.
Method
1. grant `client:idle` per `SelfIntroduction`.
Astro (`index.astro`)
└── TSX (`SelfIntroduction.tsx`) `client:idle` add
└──
TSX (`MyIcon.tsx`)
Give `client:idle` to `MyIcon` only (current method)
Astro (`index.astro`)
├──
TSX (`SelfIntroduction.tsx`)
└──
TSX (`MyIcon.tsx`) `client:idle` add pass to `SelfIntroduction`
。
[ 34 / 46 ]


# Page. 37

![Page Image](https://bcdn.docswell.com/page/K7429XZ3J1.jpg)

The introductory section comes up for a moment but
disappears quickly...
🫨
1. grant `client:idle` per `SelfIntroduction`.
method somehow didn&#039;t work.
(We have not been able to identify the cause of the problem...)
[ 35 / 46 ]


# Page. 38

![Page Image](https://bcdn.docswell.com/page/LJ1MKNRZEG.jpg)

Organizing so far
Currently calling `MyIcon` from Astro and passing it to `SelfIntroduction`.
This method does not give a uniform probability of animation, so we want to use one component and
one element.
Easier to make it 1 component 1 element by calling `MyIcon` from `SelfIntroduction`.
but this method makes the drawing look strange.
What to do
[ 36 / 46 ]


# Page. 39

![Page Image](https://bcdn.docswell.com/page/GJW3D416J2.jpg)

Organizing so far
Currently calling `MyIcon` from Astro and passing it to `SelfIntroduction`.
This method does not give a uniform probability of animation, so we want to use one component and
one element.
Easier to make it 1 component 1 element by calling `MyIcon` from `SelfIntroduction`.
but this method makes the drawing look strange.
What to do
Using React’s `useState` to make it manageable for multiple `MyIcon`s to handle multiple elements
Convert `SelfIntroduction.tsx` containing `MyIcon` calls to `.astro` files.
[ 36 / 46 ]


# Page. 40

![Page Image](https://bcdn.docswell.com/page/4EZVG2PRJ3.jpg)

Use React’s useState to make it manageable for
multiple MyIcon s to handle multiple elements.
I used to use this method. I stopped while I was working on the documents.
useState`and`useRef` to manage the current animation state of large and small icons respectively
Only one animation class is now added for each large and small icon
Complex structure
(I was trying out some stuff at the time, but I can’t remember why I needed `useRef`.)
[ 37 / 46 ]


# Page. 41

![Page Image](https://bcdn.docswell.com/page/Y765YVM1EV.jpg)

`MyIcon.tsx`
export const MyIcon = (props: IMyIconProps) =&gt; {
const [largeMode, setLargeMode] = useState(&#039;rotate-animation-z&#039;);
const largeModeRef = useRef&lt;string&gt;(null!);
largeModeRef.current = largeMode;
const [smallMode, setSmallMode] = useState(&#039;rotate-animation-z&#039;);
const smallModeRef = useRef&lt;string&gt;(null!);
smallModeRef.current = smallMode;
useEffect(makeEffect(&#039;my-icon-large&#039;, setLargeMode, largeModeRef));
useEffect(makeEffect(&#039;my-icon-small&#039;, setSmallMode, smallModeRef));
return (
&lt;img
src={props.iconPath}
style={{ width: &#039;100%&#039; }}
alt=&quot;Avatar image&quot;
loading=&quot;lazy&quot;
/&gt;
);
};
Memorial service
🪦.
[ 38 / 46 ]


# Page. 42

![Page Image](https://bcdn.docswell.com/page/G75YGDZL74.jpg)

`MyIcon.tsx`
const makeEffect = (
id: string,
setMode: React.Dispatch&lt;React.SetStateAction&lt;string&gt;&gt;,
modeRef: React.MutableRefObject&lt;string&gt;
) =&gt; {
return () =&gt; {
const thisImg = document.getElementById(id);
if (thisImg === null) return () =&gt; {};
const start = () =&gt; {
cssNames.forEach((c) =&gt; {
thisImg.classList.remove(c);
});
const randNum = Math.floor(Math.random() * cssNames.
const cssName = cssNames[randNum] || &#039;rotate-animati
setMode(cssName);
thisImg.classList.add(modeRef.current);
};
thisImg.addEventListener(&#039;click&#039;, start);
thisImg.addEventListener(&#039;animationend&#039;, end);
thisImg.addEventListener(&#039;animationcancel&#039;, end);
return () =&gt; {
thisImg.removeEventListener(&#039;click&#039;, start);
thisImg.removeEventListener(&#039;animationend&#039;, end);
thisImg.removeEventListener(&#039;animationcancel&#039;, end);
};
};
};
Memorial service
🪦.
const end = () =&gt; {
cssNames.forEach((cssName) =&gt; {
thisImg.classList.remove(cssName);
});
};
[ 39 / 46 ]


# Page. 43

![Page Image](https://bcdn.docswell.com/page/9J2GY8R3JR.jpg)

Convert SelfIntroduction.tsx containing
MyIcon calls to .astro files.
As we were making the materials, we realized that this way was simpler and easier to explain, so we
adopted it.
Fixed `MyIcon` to be one component, one element.
Changed to a format where Id is passed from outside (initial contents of large icon looking around)
Rewrite `SelfIntroduction.tsx` in `.astro` format
Call `SelfIntroduction.astro` from `index.astro`.
Call `MyIcon` from the newly created `SelfIntroduction.astro` and give `MyIcon` `client:idle
[ 40 / 46 ]


# Page. 44

![Page Image](https://bcdn.docswell.com/page/DEYVG6D8EM.jpg)

`SelfIntroduction.astro`
--// &lt;Omit: various imports&gt;.
import { MyIcon } from &#039;@/components/MyIconRandomLargeOnly&#039;;
const iconPath = /* icon path */;
// &lt;Omit: Other processing&gt;.
--&lt;Section&gt;
&lt;div className=&quot;flex flex-col items-center md:flex-row md:justify-between md:gap-x-24&quot;&gt;
&lt;div&gt;
&lt;h1 className=&quot;hidden text-3xl font-bold md:block&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;flex flex-row justify-between md:hidden md:gap-x-24&quot;&gt;
{/* For mobile phone display */}
&lt;h1 className=&quot;text-3xl font-bold&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div class=&quot;my-icon h-20 w-20&quot; id=&quot;my-icon-small&quot;&gt;
&lt;MyIcon client:idle iconPath={iconPath} iconId=&quot;my-icon-small&quot; /&gt; {/* Small icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;p className=&quot;mt-6 text-xl leading-9&quot;&gt; {/* Self-introduction */} &lt;/p&gt;
&lt;div className=&quot;mt-3 flex flex-wrap gap-1&quot;&gt; {/* SNS Links */} &lt;/div&gt;
&lt;/div&gt;
&lt;div className=&quot;hidden shrink-0 md:block&quot;&gt;
&lt;div class=&quot;my-icon h-72 w-72&quot; id=&quot;my-icon-large&quot;&gt;
&lt;MyIcon client:idle iconPath={iconPath} iconId=&quot;my-icon-large&quot; /&gt; {/* Big icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/Section&gt;
[ 41 / 46 ]


# Page. 45

![Page Image](https://bcdn.docswell.com/page/VJNMGZ6978.jpg)

`SelfIntroduction.astro`
--// &lt;Omit: various imports&gt;.
import { MyIcon } from &#039;@/components/MyIconRandomLargeOnly&#039;;
const iconPath = /* icon path */;
// &lt;Omit: Other processing&gt;.
--&lt;Section&gt;
&lt;div className=&quot;flex flex-col items-center md:flex-row md:justify-between md:gap-x-24&quot;&gt;
&lt;div&gt;
&lt;h1 className=&quot;hidden text-3xl font-bold md:block&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div className=&quot;flex flex-row justify-between md:hidden md:gap-x-24&quot;&gt;
{/* For mobile phone display */}
&lt;h1 className=&quot;text-3xl font-bold&quot;&gt; {/* Greetings */} &lt;/h1&gt;
&lt;div class=&quot;my-icon h-20 w-20&quot; id=&quot;my-icon-small&quot;&gt;
&lt;MyIcon client:idle iconPath={iconPath} iconId=&quot;my-icon-small&quot; /&gt; {/* Small icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;p className=&quot;mt-6 text-xl leading-9&quot;&gt; {/* Self-introduction */} &lt;/p&gt;
&lt;div className=&quot;mt-3 flex flex-wrap gap-1&quot;&gt; {/* SNS Links */} &lt;/div&gt;
&lt;/div&gt;
&lt;div className=&quot;hidden shrink-0 md:block&quot;&gt;
&lt;div class=&quot;my-icon h-72 w-72&quot; id=&quot;my-icon-large&quot;&gt;
&lt;MyIcon client:idle iconPath={iconPath} iconId=&quot;my-icon-large&quot; /&gt; {/* Big icon */}
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/Section&gt;
[ 41 / 46 ]


# Page. 46

![Page Image](https://bcdn.docswell.com/page/YE9NWZL3E3.jpg)

index.astro
--// &lt;Omit: various imports&gt;.
import SelfIntroduction from &#039;@/components/SelfIntroduction.astro&#039;;
// &lt;Omit: Other processing&gt;.
--...
&lt;SelfIntroduction /&gt;
...
[ 42 / 46 ]


# Page. 47

![Page Image](https://bcdn.docswell.com/page/GE8KN1XL7D.jpg)

One icon no longer has multiple animation classes
🥳.
[ 43 / 46 ]


# Page. 48

![Page Image](https://bcdn.docswell.com/page/LELZPQ8QJR.jpg)

Try turning it into a .astro file.
What was good
Replacing `.tsx` with `.astro` was easy.
The `.astro` makes it simpler to implement if you want to dynamically execute JS.
What’s not so good
Cannot call `.astro` from `.tsx`.
You may have trouble in the future.
It would be good to keep only the parts that you actually want to be dynamic in `.tsx`, and wrap
them in `.astro` for placement.
[ 44 / 46 ]


# Page. 49

![Page Image](https://bcdn.docswell.com/page/4JMV41XKJW.jpg)

Summary
Can rotate images using CSS.
If you want the animation to rotate with each click, you can add rotation CSS on click and remove it when
the animation ends.
If you want to rotate randomly, and for some reason want to place multiple components of the same
image, and you are using Astro, you will have to twist your head.
If you want to execute JS dynamically in Astro, be careful where you write `client:*`.
It would be nice to have `.astro` for the part that calls the dynamic component
If you know of a better way, please let me know!
[ 45 / 46 ]


# Page. 50

![Page Image](https://bcdn.docswell.com/page/PJR2WLN6E9.jpg)

Thank you for your attention!
By the way, this slide was created by Slidev[1][2].
1. https://sli.dev/
2. https://github.com/korosuke613/zenn-articles/pull/376
[ 46 / 46 ]


