Added WIP Menu

This commit is contained in:
2025-09-29 15:20:46 +02:00
parent 1773687814
commit bfbe687b63
56 changed files with 4726 additions and 1070 deletions

View File

@@ -1,93 +1,77 @@
.header {
@mixin py var(--spacing-tight);
@layer components {
.header {
@mixin py var(--el-header-paddingY);
width: var(--dim-full);
color: var(--color-text-inverse);
background-color: var(--color-surface-inverse);
}
position: sticky;
z-index: 9;
top: 0;
.inner {
@mixin responsive-wrapper;
width: var(--dim-full);
display: flex;
flex-direction: row;
gap: var(--spacing-cozy);
align-items: center;
justify-content: flex-start;
}
color: var(--color-text-inverse);
.logo {
font-family: var(--font-mono);
animation:
logo-pulse 5s cubic-bezier(0.4, 0, 0.6, 1) infinite,
logo-glitch 15s linear infinite;
animation-delay: 0s, 3s;
&:hover {
transform: translate(0, 0);
color: var(--color-secondary);
opacity: 1;
transition: all 0.2s ease;
animation: none;
background-color: var(--color-surface-inverse);
}
}
.pagename {
font-family: var(--font-mono);
.inner {
@mixin responsive-wrapper;
display: flex;
flex-direction: row;
gap: var(--spacing-cozy);
align-items: center;
justify-content: flex-start;
font-size: var(--el-header-font-size);
line-height: var(--el-header-line-height);
& .bracket {
color: var(--color-secondary);
}
}
.menutoggle {
@mixin ml auto;
.logo {
@mixin anim-txt-characterglitch;
& button {
cursor: pointer;
font-family: var(--font-mono);
transition: color 0.2s ease-out;
animation:
logo-pulse 5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
&:hover {
color: var(--color-primary);
}
&:active {
transform: scale(0.95);
transition: transform 0.1s ease-out;
transform: translate(0, 0);
color: var(--color-secondary);
}
}
}
@keyframes logo-pulse {
0% { opacity: 1; }
25% { opacity: 0.66; }
50% { opacity: 0.33; }
75% { opacity: 0.66; }
100% { opacity: 1; }
}
.pagename {
font-family: var(--font-mono);
@keyframes logo-glitch {
0% {
transform: translate(0, 0);
color: inherit;
& .bracket {
color: var(--color-secondary);
}
}
2% {
transform: translate(-2px, -2px);
color: var(--color-primary);
.menutoggle {
@mixin ml auto;
& button {
cursor: pointer;
font-family: var(--font-mono);
transition: color 0.2s ease-out;
&:hover {
color: var(--color-primary);
}
&:active {
transform: scale(0.95);
transition: transform 0.1s ease-out;
}
}
}
4% {
transform: translate(0, 0);
color: inherit;
}
100% {
transform: translate(0, 0);
color: inherit;
@keyframes logo-pulse {
0% { opacity: 1; }
25% { opacity: 0.66; }
50% { opacity: 0.33; }
75% { opacity: 0.66; }
100% { opacity: 1; }
}
}

View File

@@ -0,0 +1,403 @@
@layer components {
/* === MenuGrid === */
.menu {
pointer-events: none;
position: fixed;
z-index: 9;
top: var(--el-header-height);
left: 0;
display: grid;
grid-auto-columns: 1fr;
grid-template-areas:
"area_1"
"area_2"
"area_3"
"area_4"
"area_5";
grid-template-columns: 1fr;
grid-template-rows: repeat(5, auto);
gap: 0;
gap: var(--spacing-cozy);
width: 100vw;
height: 100%;
max-height: calc(100vh - var(--el-header-height));
visibility: hidden;
opacity: 0;
background-color: var(--color-palette-charcoal-gray);
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
&.isOpen {
pointer-events: auto;
visibility: visible;
opacity: 1;
}
@media screen and (--bp-tablet) {
@mixin px var(--spacing-comfortable);
grid-template-areas:
"area_1"
"area_2"
"area_3"
"area_4"
"area_5";
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: var(--spacing-comfortable);
}
@media screen and (--bp-desktop) {
display: grid;
grid-template-areas:
"area_4 area_4 area_3 area_3 area_3 area_3 area_2"
"area_4 area_4 area_3 area_3 area_3 area_3 area_2"
"area_5 area_5 area_3 area_3 area_3 area_3 area_2"
"area_5 area_5 area_3 area_3 area_3 area_3 area_1"
"area_5 area_5 area_3 area_3 area_3 area_3 area_1";
grid-template-columns: 2.5fr 1fr 3fr 1.5fr 1fr 1fr 4fr;
grid-template-rows: 3fr 1fr 2.5fr 1.5fr 2fr;
}
}
/* === MenuArea === */
.area {
/* === AREA VARIABLES === */
--area-bg: transparent;
--area-border: transparent;
--area-animation-keyframe: none;
--area-animation-duration: 0s;
--area-animation-timing: linear;
--area-bg-filter: grayscale(100%) contrast(150) brightness(150);
/** === TITLE VARIABLES === */
--title-color: var(--color-palette-light-silver);
--title-font: var(--font-header);
--title-font-size: var(--typo-size-2xl);
--title-font-weight: var(--typo-weight-black);
--title-line-height: 1;
--title-transform: uppercase;
--title-spacing: var(--typo-spacing-comfortable);
--title-hover-color: var(--color-tertiary);
--title-current-color: var(--color-primary);
--title-current-bg: var(--color-primary);
/** === SUBLINK VARIABLES === */;
--sublink-color: var(--color-palette-light-silver);
--sublink-font: var(--font-header);
--sublink-font-size: var(--typo-size-2xl);
--sublink-font-weight: var(--typo-weight-black);
--sublink-transform: uppercase;
--sublink-spacing: var(--typo-spacing-relaxed);
--sublink-line-height: var(--typo-leading-relaxed);
--sublink-letter-spacing: var(--typo-spacing-relaxed);
--sublink-current-color: var(--color-primary);
--sublink-current-bg: var(--color-primary);
/** === SUBTITLE VARIABLES === **/
--divider-color: var(--color-palette-light-silver);
--divider-width: var(--size-6);
--divider-height: var(--size-2);
--divider-font: var(--font-mono);
--divider-symbol: ;
--divider-font-size: var(--typo-size-2xl);
--divider-padding: 0 var(--typo-spacing-cozy);
--subtitle-color: var(--color-palette-light-silver);
--subtitle-font: var(--font-mono);
--subtitle-font-size: var(--typo-size-2xl);
--subtitle-text-transform: uppercase;
--subtitle-letter-spacing: var(--typo-spacing-cozy);
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: var(--size-1) solid var(--area-border);
text-align: center;
background: var(--area-bg);
transition: all 0.2s ease-in-out;
&:not(.current) {
&:hover {
animation: var(--area-animation-keyframe) var(--area-animation-duration) var(--area-animation-timing);
}
}
}
.area_1 { grid-area: area_1; }
.area_2 { grid-area: area_2; }
.area_3 { grid-area: area_3; }
.area_4 { grid-area: area_4; }
.area_5 { grid-area: area_5; }
.hasBGImg {
&:hover {
.bgImg {
animation: var(--area-animation-keyframe) var(--area-animation-duration) var(--area-animation-timing);
}
}
}
.bgImg {
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
object-fit: cover;
filter: var(--area-bg-filter);
@media screen and (--bp-tablet-down) {
display: none;
}
}
/* === MenuTitle === */
.title {
position: relative;
}
.mainlink {
position: relative;
font-family: var(--title-font);
font-size: var(--title-font-size);
font-weight: var(--title-font-weight);
line-height: var(--title-line-height);
color: var(--title-color);
text-transform: var(--title-transform);
letter-spacing: var(--title-spacing);
transition: any 0.5s ease-in-out;
&:focus {
&:hover {
color: var(--title-current-color);
}
@media screen and (--bp-tablet-down) {
outline: none;
&:hover {
color: var(--title-current-color);
}
}
}
&.current {
pointer-events: none;
@media screen and (--bp-tablet-down) {
&:hover {
color: var(--title-current-color);
}
}
@media screen and (--bp-desktop) {
}
}
@media screen and (--bp-tablet-down) {
transition: border 0.5s ease-in-out;
}
}
/* === MenuSublinks === */
.list {
@media screen and (--bp-tablet-down) {
display: none;
}
}
.item {
position: relative;
}
.sublink {
position: relative;
font-family: var(--sublink-font);
font-size: var(--sublink-font-size);
font-weight: var(--sublink-font-weight);
line-height: var(--sublink-line-height);
color: var(--sublink-color);
text-transform: var(--sublink-transform);
letter-spacing: var(--sublink-spacing);;
&:not(.current),
&:not(.focus) {
transition: var(--sublink-hover-transition);
&::after,
&::before {
position: absolute;
font-family: var(--sublink-hover-decorator-font);
opacity: 0;
transition: var(--sublink-hover-decorator-transition);
}
&::before {
content: var(--sublink-hover-decorator-left-symbol);
left: 0;
}
&::after {
content: var(--sublink-hover-decorator-right-symbol);
right: 0;
}
&:hover {
color: var(--sublink-hover-color);
&::before {
top: var(--sublink-hover-decorator-left-pos-y);
left: var(--sublink-hover-decorator-left-pos-x);
transform: var(--sublink-hover-decorator-left-transform);
opacity: 1;
}
&::after {
top: var(--sublink-hover-decorator-right-pos-y);
right: var(--sublink-hover-decorator-rightt-pos-x);
transform: var(--sublink-hover-decorator-right-transform);
opacity: 1;
}
}
&:focus {
&:hover {
color: var(--sublink-current-color);
}
}
&.current {
pointer-events: none;
}
}
}
/* === MenuSubtitle === */
.wrapper {
position: relative;
@media screen and (--bp-tablet-down) {
display: none;
}
}
/* === UTILITY Classes */
@media screen and (--bp-desktop) {
.pos_tr {
position: absolute;
top: 1em;
right: 1em;
}
.pos_tc {
position: absolute;
top: 1em;
left: 50%;
transform: translateX(-50%);
}
.pos_tl {
position: absolute;
top: 1em;
left: 1em;
}
.pos_cr {
position: absolute;
top: 50%;
right: 1em;
transform: translateY(-50%);
}
.pos_c {
position: absolute;
bottom: 50%;
left: 50%;
transform: translate(-50%, 50%);
}
.pos_cl {
position: absolute;
top: 50%;
right: 1em;
transform: translateY(-50%);
}
.pos_br {
position: absolute;
right: 1em;
bottom: 1em;
}
.pos_bc {
position: absolute;
bottom: 1em;
left: 50%;
transform: translateX(-50%);
}
.pos_bl {
position: absolute;
bottom: 1em;
left: 1em;
}
.vertical-rl {
writing-mode: vertical-rl;
& * {
writing-mode: vertical-rl;
}
}
.vertical-lr {
writing-mode: vertical-lr;
& * {
writing-mode: vertical-lr;
}
}
.sideways-rl {
writing-mode: sideways-rl;
& * {
writing-mode: sideways-rl;
}
}
.sideways-lr {
writing-mode: sideways-lr;
& * {
writing-mode: sideways-lr;
}
}
}
/* === CATEGORY Variants === */
@media screen and (--bp-desktop) {
.meta {}
.kitchensink {}
.awq {}
.worldburner {}
.chainbreaker {}
}
}

View File

@@ -0,0 +1,121 @@
@layer components {
.area {
--area-bg: transparent;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background: var(--area-bg);
@media screen and (--bp-tablet-down) {
border-bottom: var(--size-1) solid var(--color-palette-gunmetal);
&:last-child {
border-bottom: none;
}
}
}
.area_1 {
position: relative;
grid-area: area_1;
}
.area_2 {
position: relative;
grid-area: area_2;
}
.area_3 {
position: relative;
grid-area: area_3;
}
.area_4 {
position: relative;
grid-area: area_4;
}
.area_5 {
position: relative;
grid-area: area_5;
}
.hasBGImg {
position: relative;
&:hover {
& .bgImg {
animation: var(--area-animation-keyframe) var(--area-animation-duration) var(--area-animation-timing);
}
}
}
.bgImg {
--area-bg-animation-keyframe: none;
--area-bg--animation-duration: 0s;
--area-bg--animation-timing: linear;
--area-bg-filter: grayscale(100%) contrast(150) brightness(150);
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
object-fit: cover;
filter: var(--area-bg-filter);
@media screen and (--bp-tablet-down) {
display: none;
}
}
@media screen and (--bp-desktop) {
.chainbreaker {
--area-animation-keyframe: var(--kf-color-bleed);
--area-animation-duration: var(--img-colorbleed-duration);
--area-animation-timing: var(--img-colorbleed-timing);
& .bgImg {
z-index: -2;
}
&.hasBGImg {
--divider-color: transparent;
--title-color: transparent;
--subtitle-color: transparent;
--subtitle-transition: all 0.3s ease-in-out;
--title-transition: color 0.3s ease-in-out;
&::before {
content: "";
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
opacity: 0;
background: alpharize(var(--grid-bg), 0.66);
}
&:hover {
--divider-color: var(--color-tertiary);
--title-color: var(--color-tertiary);
--subtitle-color: var(--color-tertiary);
&::before {
opacity: 1;
}
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import MenuItem from '@/components/Page/Menu//MenuItem/';
import { useCurrentPath } from '@/hooks/useCurrentPath';
import styles from './MenuArea.module.css';
import { NavigationItem } from '@/lib/types/navigation';
interface MenuAreaProps {
item: NavigationItem;
}
const MenuArea = React.memo(({ item }: MenuAreaProps) => {
const hasBGImg = !!item.background;
const { isCurrentPath } = useCurrentPath();
const areaClasses = React.useMemo(() => {
return [
styles.area,
styles[item.gridPosition],
styles[item.variant],
isCurrentPath(item.path) ? styles.current : '',
item.background ? styles.hasBGImg : null,
]
.filter(Boolean)
.join(' ');
}, [item.background, isCurrentPath]);
return (
<section className={areaClasses}>
{hasBGImg && (
<img
className={styles.bgImg}
src={item.background}
alt={`Background Image for ${item.name}`}
/>
)}
<MenuItem item={item} />
</section>
);
});
MenuArea.displayName = 'MenuArea';
export default MenuArea;

View File

@@ -0,0 +1,97 @@
@layer components {
.menu {
--grid-bg: var(--color-palette-charcoal-gray);
--grid-fg: var(--color-palette-light-silver);
/* === MenuTitle Vars === */
--title-color: var(--grid-fg);
--title-font: var(--font-mono);
--title-font-size: var(--typo-size-2xl);
--title-font-weight: var(--typo-weight-black);
--title-line-height: 1;
--title-transform: uppercase;
--title-spacing: var(--typo-spacing-comfortable);
--title-hover-fg: var(--primary);
--title-hover-bg: transparent;
--title-current-bg: var(--color-tertiary);
--title-current-fg: var(--grid-bg);
--title-focus-fg: var(--color-secondary);
--title-focus-bg: var(--grid-bg);
--title-transition: none;
/* === MenuSublinks Vars === */
--sublink-color: var(--grid-fg);
--sublink-font: var(--font-mono);
--sublink-font-size: var(--typo-size-xl);
--sublink-font-weight: var(--typo-weight-light);
--sublink-transform: uppercase;
--sublink-line-height: var(--typo-leading-relaxed);
--sublink-spacing: var(--typo-spacing-loosest);
--sublink-hover-fg: var(--color-tertiary);
--sublink-hover-bg: transparent;
--sublink-current-fg: var(--grid-bg);
--sublink-current-bg: var(--color-primary);
--sublink-focus-fg: var(--color-secondary);
--sublink-focus-bg: var(--grid-bg);
--sublink-transition: none;
/* === MenuSubtitle Vars === */
--divider-color: var(--grid-fg);
--divider-width: var(--size-12);
--divider-height: var(--size-2);
--divider-font: var(--font-mono);
--divider-font-size: var(--typo-size-2xl);
--divider-padding: 0 var(--typo-spacing-cozy);
--subtitle-font: var(--font-mono);
--subtitle-color: var(--grid-fg);
--subtitle-font-size: var(--typo-size-xl);
--subtitle-text-transform: uppercase;
--subtitle-letter-spacing: var(--typo-spacing-cozy);
--subtitle-transition: none;
pointer-events: none;
position: fixed;
z-index: 9;
top: var(--el-header-height);
left: 0;
display: grid;
grid-auto-columns: 1fr;
grid-template-areas:
"area_1"
"area_2"
"area_3"
"area_4"
"area_5";
grid-template-columns: 1fr;
grid-template-rows: repeat(5, auto);
width: 100vw;
height: calc(100vh - var(--el-header-height));
color: var(--grid-fg);
visibility: hidden;
opacity: 0;
background-color: var(--grid-bg);
&.isOpen {
pointer-events: auto;
visibility: visible;
opacity: 1;
}
@media screen and (--bp-desktop) {
display: grid;
grid-template-areas:
"area_4 area_4 area_3 area_3 area_3 area_3 area_2"
"area_4 area_4 area_3 area_3 area_3 area_3 area_2"
"area_5 area_5 area_3 area_3 area_3 area_3 area_2"
"area_5 area_5 area_3 area_3 area_3 area_3 area_1"
"area_5 area_5 area_3 area_3 area_3 area_3 area_1";
grid-template-columns: 2.5fr 1fr 3fr 1.5fr 1fr 1fr 4fr;
grid-template-rows: 3fr 1fr 2.5fr 1.5fr 2fr;
}
}
}

View File

@@ -0,0 +1,67 @@
'use client';
import React from 'react';
import { useMenu } from '@/contexts/MenuContext';
import MenuArea from '@/components/Page/Menu/MenuArea/';
import styles from './MenuGrid.module.css';
interface MenuGridProps {
navigationData: Awaited<
ReturnType<
typeof import('@/lib/readers/system/navigation').getNavigationData
>
>;
}
export default function MenuGrid({ navigationData }: MenuGridProps) {
const { isMenuOpen, closeMenu } = useMenu();
const menuRef = React.useRef<HTMLElement>(null);
const [isClosing, setIsClosing] = React.useState(false);
const handleClose = React.useCallback(() => {
setIsClosing(true);
setTimeout(() => {
closeMenu();
setIsClosing(false);
}, 800);
}, [closeMenu]);
React.useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isMenuOpen) {
handleClose();
}
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isMenuOpen, handleClose]);
React.useEffect(() => {
if (isMenuOpen && menuRef.current) {
const initialFocus = menuRef.current.querySelector(
'a, button'
) as HTMLElement;
initialFocus?.focus();
}
}, [isMenuOpen]);
if (!navigationData) return null;
const { items } = navigationData;
return (
<nav
ref={menuRef}
className={`${styles.menu} ${isMenuOpen ? styles.isOpen : ''} ${isClosing ? styles.isClosing : ''}`}
aria-hidden={!isMenuOpen}
aria-label="Main navigation"
>
{items.map((item, index) => (
<MenuArea key={item.gridPosition} item={item} />
))}
</nav>
);
}

View File

@@ -0,0 +1,24 @@
import { hasSublinks, hasSubtitle } from '@/lib/types/navigation';
import MenuTitle from '@/components/Page/Menu/MenuTitle/';
import MenuSublinks from '@/components/Page/Menu/MenuSublinks/';
import MenuSubtitle from '@/components/Page/Menu/MenuSubtitle/';
import { NavigationItem } from '@/lib/types/navigation';
interface MenuItem {
item: NavigationItem;
}
export default function MenuItem({ item }: MenuItem) {
return (
<>
<MenuTitle path={item.path} name={item.name} variant={item.variant} />
{hasSublinks(item) && (
<MenuSublinks sublinks={item.sublinks.value} variant={item.variant} />
)}
{hasSubtitle(item) && (
<MenuSubtitle subtitle={item.subtitle.value} variant={item.variant} />
)}
</>
);
}

View File

@@ -0,0 +1,57 @@
@layer components {
.list {
@media screen and (--bp-tablet-down) {
@util hide-visually;
}
}
.item {
position: relative;
}
.sublink {
font-family: var(--sublink-font);
font-size: var(--sublink-font-size);
font-weight: var(--sublink-font-weight);
line-height: var(--sublink-line-height);
color: var(--sublink-color);
text-transform: var(--sublink-transform);
letter-spacing: var(--sublink-spacing);
&.current {
padding: var(--spacing-snug) var(--spacing-tight);
color: var(--title-current-fg);
animation: var(--kf-text-glitch) 5s infinite;
&::before {
content: "⟩";
}
}
&:not(.current) {
--pointer-left-symbol: "▶";
--pointer-right-symbol: "◀";
--pointer-distance: 1em;
@mixin anim-txt-pointer_focus;
@mixin px var(--spacing-tight);
&:focus {
color: var(--sublink-focus-fg);
outline: none;
}
&:not(:focus) {
--pointer-left-symbol: "{";
--pointer-right-symbol: "}";
--pointer-distance: 0.5em;
@mixin anim-txt-pointer;
&:hover {
--sublink-color: var(--color-primary);
}
}
}
}
}

View File

@@ -0,0 +1,29 @@
import Link from 'next/link';
import styles from './MenuSublinks.module.css';
import { SubNavigationItem } from '@/lib/types/navigation';
import { useCurrentPath } from '@/hooks/useCurrentPath';
interface MenuSublinks {
sublinks: readonly SubNavigationItem[];
variant: string;
}
export default function MenuSublinks({ sublinks, variant }: MenuSublinks) {
const { isCurrentPath } = useCurrentPath();
return (
<ul className={`${styles.list} ${styles[`${variant}`]}`}>
{sublinks.map((sublink: SubNavigationItem) => (
<li className={styles.item} key={sublink.path}>
<Link
href={sublink.path}
className={`${styles.sublink} ${isCurrentPath(sublink.path) ? styles.current : ''}`}
>
{sublink.name}
</Link>
</li>
))}
</ul>
);
}

View File

@@ -0,0 +1,57 @@
@layer components {
.claim {
@media screen and (--bp-tablet-down) {
display: none;
}
}
.divider {
position: relative;
width: 100%;
text-align: center;
}
.dividersymbol {
position: relative;
font-family: var(--divider-font);
font-size: var(--divider-font-size);
line-height: var(--divider-line-height);
color: var(--divider-color);
transition: var(--subtitle-transition);
&::before,
&::after {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
width: var(--divider-width);
height: var(--divider-height);
background-color: var(--divider-color);
}
&::before {
left: calc(var(--divider-width) *-1);
}
&::after {
right: calc(var(--divider-width) *-1);
}
}
.subtitle {
font-family: var(--subtitle-font);
font-size: var(--subtitle-font-size);
color: var(--subtitle-color);
text-transform: var(--subtitle-text-transform);
letter-spacing: var(--subtitle-letter-spacing);
transition: var(--subtitle-transition);
}
}

View File

@@ -0,0 +1,26 @@
import { Subtitle, hasDivider } from '@/lib/types/navigation';
import styles from './MenuSubtitle.module.css';
interface MenuSubtitleProps {
subtitle: Subtitle;
variant: string;
}
export default function MenuSubtitle({ subtitle, variant }: MenuSubtitleProps) {
const divider = hasDivider(subtitle) ? (
<div className={styles.divider}>
<span className={`${styles.dividersymbol}`}>
{subtitle.divider.value}
</span>
</div>
) : (
''
);
return (
<div className={`${styles.claim} ${styles[`${variant}`]}`}>
{divider}
<p className={`${styles.subtitle}`}>{subtitle.content}</p>
</div>
);
}

View File

@@ -0,0 +1,63 @@
@layer components {
.heading {
position: relative;
&:has(+ ul) {
@mixin my var(--spacing-comfortable);
}
}
.mainlink {
position: relative;
border: none;
font-family: var(--title-font);
font-size: var(--title-font-size);
font-weight: var(--title-font-weight);
line-height: var(--title-line-height);
color: var(--title-color);
text-transform: var(--title-transform);
letter-spacing: var(--title-spacing);
background: transparent;
transition: var(--title-transition);
&.current {
padding: var(--spacing-snug) var(--spacing-tight);
color: var(--title-current-fg);
animation: var(--kf-text-glitch) 5s infinite;
&::before {
content: "⟩";
}
}
&:not(.current) {
--pointer-left-symbol: "▶";
--pointer-right-symbol: "◀";
--pointer-distance: 1em;
@mixin anim-txt-pointer_focus;
@mixin px var(--spacing-tight);
&:focus {
color: var(--title-focus-fg);
outline: none;
}
&:not(:focus) {
--pointer-left-symbol: "{";
--pointer-right-symbol: "}";
--pointer-distance: 0.5em;
@mixin anim-txt-pointer;
&:hover {
--title-color: var(--color-primary);
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
import Link from 'next/link';
import styles from './MenuTitle.module.css';
import { useCurrentPath } from '@/hooks/useCurrentPath';
interface MenuTitleProps {
name: string;
path: string;
variant: string;
}
export default function MenuTitle({ name, path, variant }: MenuTitleProps) {
const { isCurrentPath } = useCurrentPath();
return (
<h2 className={`${styles.heading} ${styles[`${variant}`]}`}>
<Link
className={`${styles.mainlink} ${isCurrentPath(path) ? styles.current : ''}`}
href={path}
>
{name}
</Link>
</h2>
);
}

View File

@@ -0,0 +1,7 @@
import { getNavigationData } from '@/lib/readers/system/navigation';
import MenuGrid from '@/components/Page/Menu/MenuGrid';
export default async function Menu() {
const navigation = await getNavigationData();
return <MenuGrid navigationData={navigation} />;
}