aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-18 11:44:37 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-18 11:44:37 +0200
commit54883bb5c36cf21462a421605a709fdd6f04b150 (patch)
treebb67a6c9ce824c52c3bae732a32f192a32969f64 /src
parentf347cc1e4ae32289198d698f05f84119a708b599 (diff)
chore: add branding animation
Diffstat (limited to 'src')
-rw-r--r--src/components/molecules/images/flipping-logo.tsx20
-rw-r--r--src/components/molecules/layout/branding.module.scss125
-rw-r--r--src/components/molecules/layout/branding.tsx26
-rw-r--r--src/components/organisms/toolbar/toolbar-items.module.scss1
-rw-r--r--src/components/organisms/toolbar/toolbar.module.scss16
-rw-r--r--src/utils/hooks/use-styles.tsx29
6 files changed, 171 insertions, 46 deletions
diff --git a/src/components/molecules/images/flipping-logo.tsx b/src/components/molecules/images/flipping-logo.tsx
index 47e54ab..1099d53 100644
--- a/src/components/molecules/images/flipping-logo.tsx
+++ b/src/components/molecules/images/flipping-logo.tsx
@@ -1,6 +1,6 @@
import Logo, { type LogoProps } from '@components/atoms/images/logo';
import Image, { type ImageProps } from 'next/image';
-import { FC } from 'react';
+import { ForwardedRef, forwardRef, ForwardRefRenderFunction } from 'react';
import styles from './flipping-logo.module.scss';
export type FlippingLogoProps = {
@@ -27,15 +27,15 @@ export type FlippingLogoProps = {
*
* Render a logo and a photo with a flipping effect.
*/
-const FlippingLogo: FC<FlippingLogoProps> = ({
- className = '',
- altText,
- logoTitle,
- photo,
- ...props
-}) => {
+const FlippingLogo: ForwardRefRenderFunction<
+ HTMLDivElement,
+ FlippingLogoProps
+> = (
+ { className = '', altText, logoTitle, photo, ...props },
+ ref: ForwardedRef<HTMLDivElement>
+) => {
return (
- <div className={`${styles.logo} ${className}`}>
+ <div className={`${styles.logo} ${className}`} ref={ref}>
<div className={styles.logo__front}>
<Image
src={photo}
@@ -52,4 +52,4 @@ const FlippingLogo: FC<FlippingLogoProps> = ({
);
};
-export default FlippingLogo;
+export default forwardRef(FlippingLogo);
diff --git a/src/components/molecules/layout/branding.module.scss b/src/components/molecules/layout/branding.module.scss
index aa18002..6121fa1 100644
--- a/src/components/molecules/layout/branding.module.scss
+++ b/src/components/molecules/layout/branding.module.scss
@@ -1,48 +1,105 @@
@use "@styles/abstracts/functions" as fun;
+@use "@styles/abstracts/mixins" as mix;
+
+@mixin typing-animation {
+ --typing-animation: none;
+
+ width: fit-content;
+ position: relative;
+ overflow: hidden;
+
+ &::after {
+ content: "|";
+ display: block;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ right: 0;
+ background: var(--color-bg);
+ color: var(--color-primary-darker);
+ font-weight: 400;
+ text-align: left;
+ visibility: hidden;
+ transform: translateX(100%);
+ transform-origin: right;
+ animation: var(--typing-animation);
+
+ :global {
+ animation: var(--typing-animation);
+ }
+ }
+}
.wrapper {
+ --logo-size: #{clamp(fun.convert-px(90), 12vw, fun.convert-px(100))};
+
display: grid;
- grid-template-columns:
- var(--logo-size, fun.convert-px(100))
- minmax(0, 1fr);
- grid-template-rows: 1fr min-content;
- align-items: center;
- column-gap: var(--spacing-sm);
-}
+ grid-template-columns: minmax(0, 1fr);
+ justify-items: center;
+ width: 100%;
-.logo {
- grid-row: span 2;
-}
+ @include mix.media("screen") {
+ @include mix.dimensions("2xs") {
+ grid-template-columns:
+ var(--logo-size, fun.convert-px(100))
+ minmax(0, 1fr);
+ grid-template-rows: 1fr min-content;
+ align-items: center;
+ justify-items: left;
+ column-gap: var(--spacing-sm);
+ width: unset;
+ }
+ }
-.title {
- font-size: var(--font-size-2xl);
-}
+ .logo {
+ grid-row: span 2;
+ margin-bottom: var(--spacing-sm);
-.baseline {
- color: var(--color-fg-light);
-}
+ @include mix.media("screen") {
+ @include mix.dimensions("2xs") {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .title {
+ font-size: clamp(var(--font-size-xl), 8vw, var(--font-size-2xl));
+ text-align: center;
-.link {
- background: linear-gradient(
- to top,
- var(--color-primary-light) fun.convert-px(5),
- transparent fun.convert-px(5)
- )
- left / 0 100% no-repeat;
- text-decoration: none;
- transition: all 0.6s ease-out 0s;
-
- &:hover,
- &:focus {
- background-size: 100% 100%;
+ @include typing-animation;
}
- &:focus {
- color: var(--color-primary-light);
+ .baseline {
+ color: var(--color-fg-light);
+ font-size: var(--font-size-lg);
+ text-align: center;
+
+ @include typing-animation;
}
- &:active {
- background-size: 0 100%;
- color: var(--color-primary-dark);
+ .link {
+ background: linear-gradient(
+ to top,
+ var(--color-primary-light) fun.convert-px(5),
+ transparent fun.convert-px(5)
+ )
+ left / 0 100% no-repeat;
+ text-decoration: none;
+ transition: all 0.6s ease-out 0s;
+
+ &:hover,
+ &:focus {
+ background-size: 100% 100%;
+ }
+
+ &:focus {
+ color: var(--color-primary-light);
+ }
+
+ &:active {
+ background-size: 0 100%;
+ color: var(--color-primary-dark);
+ }
}
}
diff --git a/src/components/molecules/layout/branding.tsx b/src/components/molecules/layout/branding.tsx
index 423c54f..9a82a74 100644
--- a/src/components/molecules/layout/branding.tsx
+++ b/src/components/molecules/layout/branding.tsx
@@ -1,6 +1,7 @@
import Heading from '@components/atoms/headings/heading';
+import useStyles from '@utils/hooks/use-styles';
import Link from 'next/link';
-import { FC } from 'react';
+import { FC, useRef } from 'react';
import { useIntl } from 'react-intl';
import FlippingLogo, { type FlippingLogoProps } from '../images/flipping-logo';
import styles from './branding.module.scss';
@@ -37,6 +38,9 @@ const Branding: FC<BrandingProps> = ({
withLink = false,
...props
}) => {
+ const baselineRef = useRef<HTMLParagraphElement>(null);
+ const logoRef = useRef<HTMLDivElement>(null);
+ const titleRef = useRef<HTMLHeadingElement | HTMLParagraphElement>(null);
const intl = useIntl();
const altText = intl.formatMessage(
{
@@ -55,6 +59,23 @@ const Branding: FC<BrandingProps> = ({
{ website: title }
);
+ useStyles({
+ property: '--typing-animation',
+ styles: 'blink 0.7s ease-in-out 0s 2, typing 4.3s linear 0s 1',
+ target: titleRef,
+ });
+ useStyles({
+ property: '--typing-animation',
+ styles:
+ 'hide-text 4.25s linear 0s 1, blink 0.8s ease-in-out 4.25s 2, typing 3.8s linear 4.25s 1',
+ target: baselineRef,
+ });
+ useStyles({
+ property: 'animation',
+ styles: 'flip-logo 9s ease-in 0s 1',
+ target: logoRef,
+ });
+
return (
<div className={styles.wrapper}>
<FlippingLogo
@@ -62,6 +83,7 @@ const Branding: FC<BrandingProps> = ({
altText={altText}
logoTitle={logoTitle}
photo={photo}
+ ref={logoRef}
{...props}
/>
<Heading
@@ -69,6 +91,7 @@ const Branding: FC<BrandingProps> = ({
level={1}
withMargin={false}
className={styles.title}
+ ref={titleRef}
>
{withLink ? (
<Link href="/">
@@ -84,6 +107,7 @@ const Branding: FC<BrandingProps> = ({
level={4}
withMargin={false}
className={styles.baseline}
+ ref={baselineRef}
>
{baseline}
</Heading>
diff --git a/src/components/organisms/toolbar/toolbar-items.module.scss b/src/components/organisms/toolbar/toolbar-items.module.scss
index fd526d6..c970b71 100644
--- a/src/components/organisms/toolbar/toolbar-items.module.scss
+++ b/src/components/organisms/toolbar/toolbar-items.module.scss
@@ -26,6 +26,7 @@
@include mix.media("screen") {
@include mix.dimensions(null, "sm") {
+ position: fixed;
left: 0;
right: 0;
}
diff --git a/src/components/organisms/toolbar/toolbar.module.scss b/src/components/organisms/toolbar/toolbar.module.scss
index cda9b37..4bcabcb 100644
--- a/src/components/organisms/toolbar/toolbar.module.scss
+++ b/src/components/organisms/toolbar/toolbar.module.scss
@@ -23,6 +23,18 @@
box-shadow: 0 fun.convert-px(-2) fun.convert-px(3) fun.convert-px(-1)
var(--color-shadow-dark);
+ :global {
+ animation: slide-in-from-bottom 0.8s ease-in-out 0s 1;
+ }
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ :global {
+ animation: slide-in-from-top 1s ease-in-out 0s 1;
+ }
+ }
+ }
+
.modal {
&--search,
&--settings {
@@ -52,8 +64,10 @@
}
.tooltip {
+ padding: calc(var(--title-height) / 2 + var(--spacing-2xs))
+ var(--spacing-2xs) var(--spacing-2xs);
top: unset;
- bottom: calc(100% + var(--spacing-xs));
+ bottom: calc(100% + var(--spacing-2xs));
transform-origin: bottom right;
}
}
diff --git a/src/utils/hooks/use-styles.tsx b/src/utils/hooks/use-styles.tsx
new file mode 100644
index 0000000..d47e9fb
--- /dev/null
+++ b/src/utils/hooks/use-styles.tsx
@@ -0,0 +1,29 @@
+import { RefObject, useEffect } from 'react';
+
+export type UseStylesProps = {
+ /**
+ * A property name or a CSS variable.
+ */
+ property: string;
+ /**
+ * The styles.
+ */
+ styles: string;
+ /**
+ * A targeted element reference.
+ */
+ target: RefObject<HTMLElement>;
+};
+
+/**
+ * Add styles to an element using a React reference.
+ *
+ * @param {UseStylesProps} props - An object with property, styles and target.
+ */
+const useStyles = ({ property, styles, target }: UseStylesProps) => {
+ useEffect(() => {
+ if (target.current) target.current.style.setProperty(property, styles);
+ }, [property, styles, target]);
+};
+
+export default useStyles;