@umituz/web-design-system 2.6.4 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-design-system",
3
- "version": "2.6.4",
3
+ "version": "2.7.1",
4
4
  "private": false,
5
5
  "description": "Web Design System - Atomic Design components (Atoms, Molecules, Organisms, Templates) for React applications",
6
6
  "main": "./src/index.ts",
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Responsive System Type Definitions
3
+ * @description Centralized breakpoint types for responsive design
4
+ */
5
+
6
+ export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
7
+
8
+ export type ScreenSize = Breakpoint;
9
+
10
+ export interface BreakpointValue {
11
+ min: number;
12
+ max?: number;
13
+ }
14
+
15
+ export interface ResponsiveProps {
16
+ xs?: boolean;
17
+ sm?: boolean;
18
+ md?: boolean;
19
+ lg?: boolean;
20
+ xl?: boolean;
21
+ '2xl'?: boolean;
22
+ }
23
+
24
+ export interface ShowProps {
25
+ above?: Breakpoint; // Show on larger screens (min-width)
26
+ below?: Breakpoint; // Show on smaller screens (max-width)
27
+ at?: Breakpoint; // Show only at this breakpoint
28
+ }
29
+
30
+ export interface HideProps {
31
+ above?: Breakpoint; // Hide on larger screens
32
+ below?: Breakpoint; // Hide on smaller screens
33
+ at?: Breakpoint; // Hide only at this breakpoint
34
+ }
35
+
36
+ export interface BreakpointState {
37
+ currentBreakpoint: Breakpoint;
38
+ isXs: boolean;
39
+ isSm: boolean;
40
+ isMd: boolean;
41
+ isLg: boolean;
42
+ isXl: boolean;
43
+ is2Xl: boolean;
44
+ isMobile: boolean; // xs, sm
45
+ isTablet: boolean; // md, lg
46
+ isDesktop: boolean; // xl, 2xl
47
+ }
48
+
49
+ export interface UseBreakpointReturn extends BreakpointState {
50
+ matches: (breakpoint: Breakpoint | Breakpoint[]) => boolean;
51
+ isGreaterThan: (breakpoint: Breakpoint) => boolean;
52
+ isLessThan: (breakpoint: Breakpoint) => boolean;
53
+ }
@@ -12,3 +12,14 @@ export type {
12
12
  ChildrenProps,
13
13
  PolymorphicProps,
14
14
  } from './component.types';
15
+
16
+ export type {
17
+ Breakpoint,
18
+ ScreenSize,
19
+ BreakpointValue,
20
+ ResponsiveProps,
21
+ ShowProps,
22
+ HideProps,
23
+ BreakpointState,
24
+ UseBreakpointReturn,
25
+ } from './breakpoint.types';
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Responsive System Constants
3
+ * @description Centralized breakpoint values matching Tailwind CSS defaults
4
+ */
5
+
6
+ import type { Breakpoint, BreakpointValue } from '../../domain/types/breakpoint.types';
7
+
8
+ /**
9
+ * Breakpoint values matching Tailwind CSS default breakpoints
10
+ * https://tailwindcss.com/docs/screens
11
+ */
12
+ export const BREAKPOINTS: Record<Breakpoint, BreakpointValue> = {
13
+ xs: { min: 0 },
14
+ sm: { min: 640 },
15
+ md: { min: 768 },
16
+ lg: { min: 1024 },
17
+ xl: { min: 1280 },
18
+ '2xl': { min: 1536 },
19
+ };
20
+
21
+ /**
22
+ * Create a media query string for a given breakpoint
23
+ * @param breakpoint - The breakpoint to create a query for
24
+ * @returns CSS media query string (e.g., "(min-width: 640px)")
25
+ */
26
+ export const createMediaQuery = (breakpoint: Breakpoint): string => {
27
+ const bp = BREAKPOINTS[breakpoint];
28
+ if (bp.max) {
29
+ return `(min-width: ${bp.min}px) and (max-width: ${bp.max}px)`;
30
+ }
31
+ return `(min-width: ${bp.min}px)`;
32
+ };
33
+
34
+ /**
35
+ * Create a max-width media query
36
+ * @param breakpoint - The breakpoint to create a max query for
37
+ * @returns CSS media query string (e.g., "(max-width: 639px)")
38
+ */
39
+ export const createMaxMediaQuery = (breakpoint: Breakpoint): string => {
40
+ const bp = BREAKPOINTS[breakpoint];
41
+ const maxValue = bp.min - 1;
42
+ return `(max-width: ${maxValue}px)`;
43
+ };
44
+
45
+ /**
46
+ * Breakpoint order for comparison operations
47
+ */
48
+ export const BREAKPOINT_ORDER: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'];
49
+
50
+ /**
51
+ * Compare two breakpoints
52
+ * @returns -1 if a < b, 0 if a === b, 1 if a > b
53
+ */
54
+ export const compareBreakpoints = (a: Breakpoint, b: Breakpoint): number => {
55
+ const indexA = BREAKPOINT_ORDER.indexOf(a);
56
+ const indexB = BREAKPOINT_ORDER.indexOf(b);
57
+ return indexA - indexB;
58
+ };
59
+
60
+ /**
61
+ * Check if breakpoint A is greater than breakpoint B
62
+ */
63
+ export const isBreakpointGreaterThan = (a: Breakpoint, b: Breakpoint): boolean => {
64
+ return compareBreakpoints(a, b) > 0;
65
+ };
66
+
67
+ /**
68
+ * Check if breakpoint A is less than breakpoint B
69
+ */
70
+ export const isBreakpointLessThan = (a: Breakpoint, b: Breakpoint): boolean => {
71
+ return compareBreakpoints(a, b) < 0;
72
+ };
@@ -11,3 +11,13 @@ export {
11
11
  SIZE_MAP,
12
12
  COLOR_MAP,
13
13
  } from './component.constants';
14
+
15
+ export {
16
+ BREAKPOINTS,
17
+ createMediaQuery,
18
+ createMaxMediaQuery,
19
+ BREAKPOINT_ORDER,
20
+ compareBreakpoints,
21
+ isBreakpointGreaterThan,
22
+ isBreakpointLessThan,
23
+ } from './breakpoint.constants';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Hide Component (Atom)
3
+ * @description Conditionally hide content based on screen breakpoint
4
+ */
5
+
6
+ import { useBreakpoint } from '../hooks/useMediaQuery';
7
+ import type { Breakpoint, HideProps } from '../../domain/types/breakpoint.types';
8
+ import type { BaseProps } from '../../domain/types';
9
+
10
+ export interface HideComponentProps extends BaseProps, HideProps {
11
+ children: React.ReactNode;
12
+ }
13
+
14
+ /**
15
+ * Conditionally hide content based on breakpoint
16
+ * @param above - Hide on screens larger than this breakpoint
17
+ * @param below - Hide on screens smaller than this breakpoint
18
+ * @param at - Hide only on this specific breakpoint
19
+ * @param children - Content to hide
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Hide on mobile (md and below)
24
+ * <Hide below="md"><DesktopNav /></Hide>
25
+ *
26
+ * // Hide on desktop (lg and above)
27
+ * <Hide above="lg"><MobileNav /></Hide>
28
+ *
29
+ * // Hide only on tablet
30
+ * <Hide at="md"><TabletContent /></Hide>
31
+ * ```
32
+ */
33
+ export const Hide = ({ above, below, at, children, className }: HideComponentProps) => {
34
+ const { matches, isGreaterThan, isLessThan } = useBreakpoint();
35
+
36
+ // Determine if content should be hidden
37
+ let shouldHide = false;
38
+
39
+ if (above) {
40
+ shouldHide = shouldHide || isGreaterThan(above);
41
+ }
42
+
43
+ if (below) {
44
+ shouldHide = shouldHide || isLessThan(below);
45
+ }
46
+
47
+ if (at) {
48
+ shouldHide = shouldHide || matches(at);
49
+ }
50
+
51
+ if (shouldHide) {
52
+ return null;
53
+ }
54
+
55
+ return <div className={className || ''}>{children}</div>;
56
+ };
57
+
58
+ Hide.displayName = 'Hide';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Show Component (Atom)
3
+ * @description Conditionally render content based on screen breakpoint
4
+ */
5
+
6
+ import { useBreakpoint } from '../hooks/useMediaQuery';
7
+ import type { Breakpoint, ShowProps } from '../../domain/types/breakpoint.types';
8
+ import type { BaseProps } from '../../domain/types';
9
+
10
+ export interface ShowComponentProps extends BaseProps, ShowProps {
11
+ children: React.ReactNode;
12
+ }
13
+
14
+ /**
15
+ * Conditionally show content based on breakpoint
16
+ * @param above - Show on screens larger than this breakpoint
17
+ * @param below - Show on screens smaller than this breakpoint
18
+ * @param at - Show only on this specific breakpoint
19
+ * @param children - Content to show
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Show on desktop (lg and above)
24
+ * <Show above="lg"><DesktopNav /></Show>
25
+ *
26
+ * // Show on mobile (sm and below)
27
+ * <Show below="md"><MobileNav /></Show>
28
+ *
29
+ * // Show only on tablet
30
+ * <Show at="md"><TabletContent /></Show>
31
+ * ```
32
+ */
33
+ export const Show = ({ above, below, at, children, className }: ShowComponentProps) => {
34
+ const { matches, isGreaterThan, isLessThan } = useBreakpoint();
35
+
36
+ // Determine if content should be shown
37
+ let shouldShow = true;
38
+
39
+ if (above) {
40
+ shouldShow = shouldShow && isGreaterThan(above);
41
+ }
42
+
43
+ if (below) {
44
+ shouldShow = shouldShow && isLessThan(below);
45
+ }
46
+
47
+ if (at) {
48
+ shouldShow = shouldShow && matches(at);
49
+ }
50
+
51
+ if (!shouldShow) {
52
+ return null;
53
+ }
54
+
55
+ return <div className={className || ''}>{children}</div>;
56
+ };
57
+
58
+ Show.displayName = 'Show';
@@ -57,3 +57,9 @@ export type { SwitchProps } from './Switch';
57
57
  export { Separator } from './Separator';
58
58
 
59
59
  export { Toggle, toggleVariants } from './Toggle';
60
+
61
+ export { Show } from './Show';
62
+ export type { ShowComponentProps } from './Show';
63
+
64
+ export { Hide } from './Hide';
65
+ export type { HideComponentProps } from './Hide';
@@ -8,7 +8,7 @@ export { useTheme } from './useTheme';
8
8
  export type { Theme, UseThemeReturn } from './useTheme';
9
9
 
10
10
  export { useMediaQuery, useBreakpoint } from './useMediaQuery';
11
- export type { Breakpoint } from './useMediaQuery';
11
+ export type { Breakpoint, UseBreakpointReturn } from '../../domain/types/breakpoint.types';
12
12
 
13
13
  export { useLocalStorage } from './useLocalStorage';
14
14
 
@@ -1,28 +1,39 @@
1
1
  /**
2
- * useMediaQuery Hook
3
- * @description Responsive breakpoint detection
2
+ * useMediaQuery & useBreakpoint Hooks
3
+ * @description Enhanced responsive breakpoint detection with helper functions
4
4
  */
5
5
 
6
- import { useEffect, useState } from 'react';
7
-
8
- export type Breakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
9
-
10
- const breakpointValues: Record<Breakpoint, number> = {
11
- sm: 640,
12
- md: 768,
13
- lg: 1024,
14
- xl: 1280,
15
- '2xl': 1536,
16
- };
6
+ import { useEffect, useState, useCallback } from 'react';
7
+ import type {
8
+ Breakpoint,
9
+ UseBreakpointReturn,
10
+ } from '../../domain/types/breakpoint.types';
11
+ import {
12
+ BREAKPOINTS,
13
+ createMediaQuery,
14
+ isBreakpointGreaterThan,
15
+ isBreakpointLessThan,
16
+ } from '../../infrastructure/constants/breakpoint.constants';
17
17
 
18
+ /**
19
+ * Simple media query hook for a single breakpoint
20
+ * @param breakpoint - The breakpoint to check
21
+ * @returns Whether the media query matches
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * const isDesktop = useMediaQuery('lg') // true on lg screens and above
26
+ * ```
27
+ */
18
28
  export function useMediaQuery(breakpoint: Breakpoint): boolean {
19
29
  const [matches, setMatches] = useState(false);
20
30
 
21
31
  useEffect(() => {
22
- const media = window.matchMedia(`(min-width: ${breakpointValues[breakpoint]}px)`);
32
+ const query = createMediaQuery(breakpoint);
33
+ const media = window.matchMedia(query);
23
34
  setMatches(media.matches);
24
35
 
25
- const listener = () => setMatches(media.matches);
36
+ const listener = (e: MediaQueryListEvent) => setMatches(e.matches);
26
37
  media.addEventListener('change', listener);
27
38
  return () => media.removeEventListener('change', listener);
28
39
  }, [breakpoint]);
@@ -30,17 +41,118 @@ export function useMediaQuery(breakpoint: Breakpoint): boolean {
30
41
  return matches;
31
42
  }
32
43
 
33
- export function useBreakpoint(): Breakpoint | null {
34
- const isSm = useMediaQuery('sm');
35
- const isMd = useMediaQuery('md');
36
- const isLg = useMediaQuery('lg');
37
- const isXl = useMediaQuery('xl');
38
- const is2xl = useMediaQuery('2xl');
39
-
40
- if (is2xl) return '2xl';
41
- if (isXl) return 'xl';
42
- if (isLg) return 'lg';
43
- if (isMd) return 'md';
44
- if (isSm) return 'sm';
45
- return null;
44
+ /**
45
+ * Enhanced breakpoint hook with helper functions
46
+ * @returns Object containing current breakpoint and helper functions
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * const { currentBreakpoint, isMobile, isDesktop } = useBreakpoint()
51
+ *
52
+ * return (
53
+ * <div>
54
+ * {isMobile && <MobileNav />}
55
+ * {isDesktop && <DesktopNav />}
56
+ * </div>
57
+ * )
58
+ * ```
59
+ */
60
+ export function useBreakpoint(): UseBreakpointReturn {
61
+ const [currentBreakpoint, setCurrentBreakpoint] = useState<Breakpoint>(() => {
62
+ // Initialize with current window size
63
+ if (typeof window === 'undefined') return 'lg';
64
+
65
+ const width = window.innerWidth;
66
+ for (const [bp, value] of Object.entries(BREAKPOINTS).reverse()) {
67
+ if (width >= value.min) return bp as Breakpoint;
68
+ }
69
+ return 'xs';
70
+ });
71
+
72
+ useEffect(() => {
73
+ // More efficient: track window resize with debounce
74
+ const updateBreakpoint = () => {
75
+ const width = window.innerWidth;
76
+ const sortedBreakpoints = Object.entries(BREAKPOINTS).sort(
77
+ ([, a], [, b]) => b.min - a.min
78
+ );
79
+
80
+ for (const [bp, value] of sortedBreakpoints) {
81
+ if (width >= value.min) {
82
+ setCurrentBreakpoint(bp as Breakpoint);
83
+ break;
84
+ }
85
+ }
86
+ };
87
+
88
+ // Initial check
89
+ updateBreakpoint();
90
+
91
+ // Debounced resize listener
92
+ let resizeTimer: ReturnType<typeof setTimeout>;
93
+ const handleResize = () => {
94
+ clearTimeout(resizeTimer);
95
+ resizeTimer = setTimeout(updateBreakpoint, 100);
96
+ };
97
+
98
+ window.addEventListener('resize', handleResize);
99
+
100
+ return () => {
101
+ window.removeEventListener('resize', handleResize);
102
+ clearTimeout(resizeTimer);
103
+ };
104
+ }, []);
105
+
106
+ // Helper functions
107
+ const matches = useCallback(
108
+ (breakpoint: Breakpoint | Breakpoint[]): boolean => {
109
+ if (Array.isArray(breakpoint)) {
110
+ return breakpoint.includes(currentBreakpoint);
111
+ }
112
+ return currentBreakpoint === breakpoint;
113
+ },
114
+ [currentBreakpoint]
115
+ );
116
+
117
+ const isGreaterThan = useCallback(
118
+ (breakpoint: Breakpoint): boolean => {
119
+ return isBreakpointGreaterThan(currentBreakpoint, breakpoint);
120
+ },
121
+ [currentBreakpoint]
122
+ );
123
+
124
+ const isLessThan = useCallback(
125
+ (breakpoint: Breakpoint): boolean => {
126
+ return isBreakpointLessThan(currentBreakpoint, breakpoint);
127
+ },
128
+ [currentBreakpoint]
129
+ );
130
+
131
+ // Computed values
132
+ const isXs = currentBreakpoint === 'xs';
133
+ const isSm = currentBreakpoint === 'sm';
134
+ const isMd = currentBreakpoint === 'md';
135
+ const isLg = currentBreakpoint === 'lg';
136
+ const isXl = currentBreakpoint === 'xl';
137
+ const is2Xl = currentBreakpoint === '2xl';
138
+
139
+ const isMobile = isXs || isSm;
140
+ const isTablet = isMd || isLg;
141
+ const isDesktop = isXl || is2Xl;
142
+
143
+ return {
144
+ currentBreakpoint,
145
+ isXs,
146
+ isSm,
147
+ isMd,
148
+ isLg,
149
+ isXl,
150
+ is2Xl,
151
+ isMobile,
152
+ isTablet,
153
+ isDesktop,
154
+ matches,
155
+ isGreaterThan,
156
+ isLessThan,
157
+ };
46
158
  }
@@ -7,6 +7,7 @@ import { useState, useEffect, useRef, useMemo } from 'react';
7
7
  // @ts-ignore - react-router-dom is a peer dependency, may not be available during package build
8
8
  import { Link, useLocation } from 'react-router-dom';
9
9
  import React from 'react';
10
+ import { Show, Hide } from '../atoms';
10
11
  import type { BaseProps } from '../../domain/types';
11
12
 
12
13
  export interface NavItem {
@@ -92,33 +93,36 @@ export const MainNavbar = ({
92
93
  </Link>
93
94
 
94
95
  {/* Desktop Menu */}
95
- <div className="hidden lg:flex items-center space-x-6">
96
- {navItemsMemo.map((item) => {
97
- const isActive = location.pathname === item.path;
98
- return (
99
- <Link
100
- key={item.path}
101
- to={item.path}
102
- className={`font-medium transition-colors transition-theme ${
103
- isActive ? 'text-primary-light' : 'text-text-secondary hover:text-primary-light'
104
- }`}
105
- >
106
- {item.name}
107
- </Link>
108
- );
109
- })}
110
- </div>
96
+ <Show above="lg">
97
+ <div className="flex items-center space-x-6">
98
+ {navItemsMemo.map((item) => {
99
+ const isActive = location.pathname === item.path;
100
+ return (
101
+ <Link
102
+ key={item.path}
103
+ to={item.path}
104
+ className={`font-medium transition-colors transition-theme ${
105
+ isActive ? 'text-primary-light' : 'text-text-secondary hover:text-primary-light'
106
+ }`}
107
+ >
108
+ {item.name}
109
+ </Link>
110
+ );
111
+ })}
112
+ </div>
113
+ </Show>
111
114
 
112
115
  {/* Actions */}
113
116
  <div className="flex items-center gap-2 md:gap-3">
114
117
  {/* Language Selector */}
115
- <div className="relative" ref={langDropdownRef}>
116
- <button
117
- onClick={() => setIsLangOpen(!isLangOpen)}
118
- className="p-2 rounded-lg bg-bg-secondary text-text-secondary hover:text-primary-light border border-border hover:border-primary-light transition-all transition-theme hidden lg:block"
119
- title={translations.language}
120
- type="button"
121
- >
118
+ <Show above="lg">
119
+ <div className="relative" ref={langDropdownRef}>
120
+ <button
121
+ onClick={() => setIsLangOpen(!isLangOpen)}
122
+ className="p-2 rounded-lg bg-bg-secondary text-text-secondary hover:text-primary-light border border-border hover:border-primary-light transition-all transition-theme"
123
+ title={translations.language}
124
+ type="button"
125
+ >
122
126
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
123
127
  <path d="m5 8 6 6" />
124
128
  <path d="m4 14 6-6 2-3" />
@@ -152,7 +156,8 @@ export const MainNavbar = ({
152
156
  ))}
153
157
  </div>
154
158
  )}
155
- </div>
159
+ </div>
160
+ </Show>
156
161
 
157
162
  {/* Theme Toggle */}
158
163
  <button
@@ -182,46 +187,51 @@ export const MainNavbar = ({
182
187
  </button>
183
188
 
184
189
  {/* GitHub */}
185
- {githubUrl && (
186
- <a
187
- href={githubUrl}
188
- target="_blank"
189
- rel="noopener noreferrer"
190
- className="hidden lg:flex items-center gap-2 px-4 py-2 bg-bg-secondary text-text-secondary rounded-lg border border-border hover:border-primary-light hover:text-text-primary transition-all transition-theme"
191
- >
192
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
193
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
194
- </svg>
195
- <span className="font-medium">{githubLabel}</span>
196
- </a>
197
- )}
190
+ <Show above="lg">
191
+ {githubUrl && (
192
+ <a
193
+ href={githubUrl}
194
+ target="_blank"
195
+ rel="noopener noreferrer"
196
+ className="flex items-center gap-2 px-4 py-2 bg-bg-secondary text-text-secondary rounded-lg border border-border hover:border-primary-light hover:text-text-primary transition-all transition-theme"
197
+ >
198
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
199
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
200
+ </svg>
201
+ <span className="font-medium">{githubLabel}</span>
202
+ </a>
203
+ )}
204
+ </Show>
198
205
 
199
206
  {/* Mobile Button */}
200
- <button
201
- onClick={() => setIsOpen(!isOpen)}
202
- className="lg:hidden text-text-secondary flex"
203
- type="button"
204
- aria-label="Toggle menu"
205
- >
206
- {isOpen ? (
207
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
208
- <path d="M18 6L6 18M6 6l12 12" />
209
- </svg>
210
- ) : (
211
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
212
- <line x1="4" x2="20" y1="12" y2="12" />
213
- <line x1="4" x2="20" y1="6" y2="6" />
214
- <line x1="4" x2="20" y1="18" y2="18" />
215
- </svg>
216
- )}
217
- </button>
207
+ <Hide above="lg">
208
+ <button
209
+ onClick={() => setIsOpen(!isOpen)}
210
+ className="text-text-secondary flex"
211
+ type="button"
212
+ aria-label="Toggle menu"
213
+ >
214
+ {isOpen ? (
215
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
216
+ <path d="M18 6L6 18M6 6l12 12" />
217
+ </svg>
218
+ ) : (
219
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
220
+ <line x1="4" x2="20" y1="12" y2="12" />
221
+ <line x1="4" x2="20" y1="6" y2="6" />
222
+ <line x1="4" x2="20" y1="18" y2="18" />
223
+ </svg>
224
+ )}
225
+ </button>
226
+ </Hide>
218
227
  </div>
219
228
  </div>
220
229
  </div>
221
230
 
222
231
  {/* Mobile Menu */}
223
- {isOpen && (
224
- <div className="lg:hidden bg-bg-secondary border-t border-border transition-theme">
232
+ <Hide above="lg">
233
+ {isOpen && (
234
+ <div className="bg-bg-secondary border-t border-border transition-theme">
225
235
  <div className="px-4 py-4 space-y-2">
226
236
  {/* Theme Toggle Mobile */}
227
237
  <button
@@ -302,7 +312,8 @@ export const MainNavbar = ({
302
312
  )}
303
313
  </div>
304
314
  </div>
305
- )}
315
+ )}
316
+ </Hide>
306
317
  </nav>
307
318
  );
308
319
  };
@@ -98,7 +98,7 @@ export const ResponsiveContainer = forwardRef<
98
98
  },
99
99
  ref
100
100
  ) => {
101
- const breakpoint = useBreakpoint();
101
+ const { currentBreakpoint: breakpoint } = useBreakpoint();
102
102
 
103
103
  // Determine current max width based on breakpoint
104
104
  const getCurrentMaxWidth = (): string => {