atomsystem 0.1.38 → 0.1.40
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/app/templates/Button/Button.tsx +7 -3
- package/app/templates/Button/button.css +106 -46
- package/app/templates/Dropdown/Dropdown.css +112 -0
- package/app/templates/Dropdown/Dropdown.tsx +103 -0
- package/app/templates/Form/Form.tsx +1 -1
- package/app/templates/Navigation/Navigation.tsx +145 -282
- package/app/templates/Navigation/navigation.css +41 -51
- package/app/templates/Sidebar/Sidebar.tsx +139 -0
- package/app/templates/Sidebar/sidebar.css +4 -0
- package/build/css/_variables.css +37 -153
- package/build/typescript/theme.ts +0 -8
- package/cli/add.js +3 -0
- package/css/text.css +11 -18
- package/package.json +9 -2
- package/tokens/tokens.json +266 -238
|
@@ -1,306 +1,169 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
2
3
|
import Link from "next/link";
|
|
3
|
-
import Image from "next/image";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
4
|
import { usePathname } from "next/navigation";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
5
|
+
import gsap from 'gsap';
|
|
6
|
+
import { useGSAP } from '@gsap/react';
|
|
7
|
+
import { useRef, createContext, useContext, useState } from "react";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
navMenu: {
|
|
12
|
-
name: string;
|
|
13
|
-
path?: string;
|
|
14
|
-
}[];
|
|
15
|
-
}
|
|
9
|
+
// Icons
|
|
10
|
+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
16
11
|
|
|
17
|
-
// Custom CSS classes are now used via css/navigation.css
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
const el = document.getElementById(targetId);
|
|
21
|
-
if (el) {
|
|
22
|
-
const isHidden = el.style.display === "none" || el.style.display === "";
|
|
23
|
-
el.style.display = isHidden ? "block" : "none";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
13
|
+
gsap.registerPlugin(useGSAP); // register the hook to avoid React version discrepancies
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
id,
|
|
30
|
-
className,
|
|
31
|
-
type = "topnav",
|
|
32
|
-
}: {
|
|
15
|
+
// Navigation을 베이스 interface로 정의
|
|
16
|
+
export interface Navigation {
|
|
33
17
|
children: React.ReactNode;
|
|
34
|
-
|
|
35
|
-
className?: string;
|
|
36
|
-
type?: "topnav" | "sidenav";
|
|
37
|
-
}) => {
|
|
38
|
-
const isMobile = useMediaQuery({ maxWidth: 767 });
|
|
39
|
-
|
|
40
|
-
const mobileClose = () => {
|
|
41
|
-
if (isMobile) {
|
|
42
|
-
if (id) {
|
|
43
|
-
handleNavToggle(id);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
18
|
+
}
|
|
47
19
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
className={`nav-container nav-${type} ${className} overflow-auto`}
|
|
52
|
-
onClick={mobileClose}
|
|
53
|
-
>
|
|
54
|
-
{children}
|
|
55
|
-
</nav>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
20
|
+
export interface NavLink extends Navigation {
|
|
21
|
+
href: string;
|
|
22
|
+
}
|
|
58
23
|
|
|
59
|
-
|
|
60
|
-
|
|
24
|
+
/**
|
|
25
|
+
* NavContextProps: 네비게이션 상태를 하위 컴포넌트와 공유하기 위한 인터페이스
|
|
26
|
+
* - isOpen: 메뉴가 열려 있는지 여부
|
|
27
|
+
* - setOpen: 메뉴 상태를 변경하는 함수
|
|
28
|
+
*/
|
|
29
|
+
interface NavContextProps {
|
|
30
|
+
isOpen: boolean;
|
|
31
|
+
setOpen: (open: boolean) => void;
|
|
32
|
+
}
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</ul>
|
|
79
|
-
);
|
|
34
|
+
// NavContext: 상위 NavItem의 상태를 하 하위 NavTrigger와 NavContainer에 전달하는 통로(우체국)
|
|
35
|
+
const NavContext = createContext<NavContextProps | undefined>(undefined);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* useNavContext: Context 정보를 쉽고 안전하게 꺼내 쓰기 위한 전용 커스텀 훅
|
|
39
|
+
* - 부모인 NavItem이 없을 경우(undefined)에도 에러가 나지 않도록 기본값을 반환하는 안전 장치 포함
|
|
40
|
+
*/
|
|
41
|
+
const useNavContext = () => {
|
|
42
|
+
const context = useContext(NavContext);
|
|
43
|
+
if (!context) {
|
|
44
|
+
// 부모를 찾지 못했을 때 앱이 멈추지 않도록 기본 상태 반환
|
|
45
|
+
return { isOpen: false, setOpen: () => {} };
|
|
46
|
+
}
|
|
47
|
+
return context;
|
|
80
48
|
};
|
|
81
49
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
targetId,
|
|
85
|
-
className,
|
|
86
|
-
}: {
|
|
87
|
-
children: React.ReactNode;
|
|
88
|
-
targetId: string;
|
|
89
|
-
className?: string;
|
|
90
|
-
}) => {
|
|
50
|
+
// 최상위 Navigation 레이아웃 컴포넌트
|
|
51
|
+
const Navigation = ({ children }: Navigation) => {
|
|
91
52
|
return (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
53
|
+
<nav className="nav-menu flex items-center justify-between">
|
|
54
|
+
{children}
|
|
55
|
+
</nav>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
97
58
|
|
|
98
|
-
|
|
59
|
+
// 로고나 브랜드명을 위한 컴포넌트
|
|
60
|
+
const NavBrand = ({children, href}: NavLink) => {
|
|
99
61
|
return (
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)}
|
|
104
|
-
>
|
|
105
|
-
<ul>
|
|
106
|
-
{navMenu.map((menu, idx) => {
|
|
107
|
-
return (
|
|
108
|
-
<li className="relative group" key={menu.name + idx}>
|
|
109
|
-
<div className={clsx("flex justify-between items-center pr-4")}>
|
|
110
|
-
<Link
|
|
111
|
-
href={menu.path || "#"}
|
|
112
|
-
className={clsx(
|
|
113
|
-
`block nav-text-standard nav-${type} hover:opacity-80 transition-opacity nav-padding`
|
|
114
|
-
)}
|
|
115
|
-
>
|
|
116
|
-
{menu.name}
|
|
117
|
-
</Link>
|
|
118
|
-
</div>
|
|
119
|
-
</li>
|
|
120
|
-
);
|
|
121
|
-
})}
|
|
122
|
-
</ul>
|
|
123
|
-
</nav>
|
|
124
|
-
);
|
|
125
|
-
};
|
|
62
|
+
<Link href={href} className="flex items-center h-full font-bold text-xl">{children}</Link>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
126
65
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}: {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
name: string;
|
|
135
|
-
path?: string;
|
|
136
|
-
subItems?: { name: string; path: string }[];
|
|
137
|
-
label?: boolean;
|
|
138
|
-
}[];
|
|
139
|
-
brand?: boolean;
|
|
140
|
-
}) => {
|
|
141
|
-
const [windowSize, setWindowSize] = useState<number>(); // 초기값 설정
|
|
142
|
-
const [toggleMenu, setToggleMenu] = useState<boolean>(true);
|
|
143
|
-
const pathname = usePathname();
|
|
144
|
-
const [openSubMenu, setOpenSubMenu] = useState<Record<string, boolean>>({});
|
|
66
|
+
/**
|
|
67
|
+
* NavItem: 개별 메뉴 항목 및 서브 메뉴의 상태를 관리하는 부모 컨테이너
|
|
68
|
+
* - 각각의 NavItem은 자신만의 isOpen 상태를 독립적으로 가짐
|
|
69
|
+
*/
|
|
70
|
+
const NavItem = ({children}: Navigation) => {
|
|
71
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
72
|
+
const setOpen = (open: boolean) => setIsOpen(open);
|
|
145
73
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
const handleResize = () => {
|
|
149
|
-
setWindowSize(window.innerWidth); // 윈도우 사이즈 업데이트
|
|
150
|
-
};
|
|
74
|
+
// 마우스가 메뉴 전체 영역을 완전히 벗어날 때 호출 (메뉴 닫기)
|
|
75
|
+
const handleMouseLeave = () => setIsOpen(false);
|
|
151
76
|
|
|
152
|
-
|
|
77
|
+
return (
|
|
78
|
+
<NavContext.Provider value={{ isOpen, setOpen }}>
|
|
79
|
+
<li
|
|
80
|
+
className={`nav-item`}
|
|
81
|
+
onMouseLeave={handleMouseLeave}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</li>
|
|
85
|
+
</NavContext.Provider>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
153
88
|
|
|
154
|
-
|
|
155
|
-
|
|
89
|
+
// 메뉴 항목들을 가로로 정렬해주는 리스트 컨테이너
|
|
90
|
+
const NavList = ({children}: Navigation) => {
|
|
91
|
+
return (
|
|
92
|
+
<ul className="flex items-center gap-0">{children}</ul>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
156
95
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
setToggleMenu(false);
|
|
162
|
-
}
|
|
163
|
-
}, []);
|
|
96
|
+
// 일반적인 링크 메뉴 컴포넌트
|
|
97
|
+
const NavLink = ({children, href}: NavLink) => {
|
|
98
|
+
const pathname = usePathname();
|
|
99
|
+
const active = pathname === href;
|
|
164
100
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}));
|
|
170
|
-
};
|
|
101
|
+
return (
|
|
102
|
+
<Link href={href} className={`nav-link ${active ? "active" : ""}`}>{children}</Link>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
171
105
|
|
|
172
|
-
|
|
106
|
+
/**
|
|
107
|
+
* NavTrigger: 서브 메뉴를 여는 트리거 버튼
|
|
108
|
+
* - 마우스를 올렸을 때(onMouseEnter) 부모 NavItem의 상태를 열림(true)으로 변경
|
|
109
|
+
*/
|
|
110
|
+
const NavTrigger = ({children}: Navigation) => {
|
|
111
|
+
const { isOpen, setOpen } = useNavContext();
|
|
112
|
+
|
|
113
|
+
// 트리거 영역에 진입했을 때만 메뉴 열기 실행
|
|
114
|
+
const handleMouseEnter = () => setOpen(true);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
className="nav-link flex items-center gap-1"
|
|
119
|
+
onMouseEnter={handleMouseEnter}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
{/* 상태에 따라 화살표 아이콘 회전 애니메이션 클래스 적용 */}
|
|
123
|
+
<KeyboardArrowDownIcon className={`nav-arrow ${isOpen ? "open" : ""}`} />
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
173
127
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
>
|
|
203
|
-
menu
|
|
204
|
-
</span>
|
|
205
|
-
</button>
|
|
206
|
-
</div>
|
|
128
|
+
/**
|
|
129
|
+
* NavContainer: 서브 메뉴의 실제 내용물이 담기는 컨테이너
|
|
130
|
+
* - GSAP을 사용하여 나타날 때 부드러운 애니메이션 효과 적용
|
|
131
|
+
*/
|
|
132
|
+
const NavContainer = ({children}: Navigation) => {
|
|
133
|
+
const { isOpen } = useNavContext();
|
|
134
|
+
const containerContentRef = useRef<HTMLUListElement>(null);
|
|
135
|
+
|
|
136
|
+
// 메뉴가 열릴 때 실행되는 GSAP 애니메이션
|
|
137
|
+
useGSAP(() => {
|
|
138
|
+
if (isOpen && containerContentRef.current) {
|
|
139
|
+
gsap.fromTo(containerContentRef.current,
|
|
140
|
+
{ opacity: 0, y: -10 }, // 시작 상태: 투명하고 살짝 위쪽
|
|
141
|
+
{ opacity: 1, y: 0, duration: 0.2, ease: "power2.out" } // 도착 상태: 불투명하고 제자리
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}, [isOpen]);
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
// 닫혀 있을 때는 아예 렌더링하지 않음
|
|
148
|
+
if (!isOpen) return null;
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div className="nav-container">
|
|
152
|
+
{/* 실제 박스 형태를 가진 내부 리스트 */}
|
|
153
|
+
<ul ref={containerContentRef} className="nav-content">
|
|
154
|
+
{children}
|
|
155
|
+
</ul>
|
|
207
156
|
</div>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
className={`nav-text-sub nav-${navType} mt-2 mb-0 nav-padding`}
|
|
222
|
-
>
|
|
223
|
-
{menu.name}
|
|
224
|
-
</p>
|
|
225
|
-
);
|
|
226
|
-
default:
|
|
227
|
-
return (
|
|
228
|
-
<li className="relative group" key={menu.name}>
|
|
229
|
-
<div
|
|
230
|
-
className={clsx("flex justify-between items-center pr-4")}
|
|
231
|
-
>
|
|
232
|
-
<Link
|
|
233
|
-
href={menu.path || "#"}
|
|
234
|
-
className={clsx(
|
|
235
|
-
`block nav-text-standard nav-${navType} nav-padding`,
|
|
236
|
-
{
|
|
237
|
-
"nav-text-active":
|
|
238
|
-
(menu.path && menu.path == pathname) ||
|
|
239
|
-
menu.subItems?.some((sub) => sub.path === pathname),
|
|
240
|
-
}
|
|
241
|
-
)}
|
|
242
|
-
>
|
|
243
|
-
{menu.name}
|
|
244
|
-
</Link>
|
|
245
|
-
{menu.subItems && type == "sidebar" ? (
|
|
246
|
-
<button
|
|
247
|
-
className="cursor-pointer"
|
|
248
|
-
onClick={() => toggleSubMenu(menu.name)}
|
|
249
|
-
>
|
|
250
|
-
<span
|
|
251
|
-
className={clsx(
|
|
252
|
-
`material-symbols-outlined nav-text-sub nav-${navType} transition-transform`,
|
|
253
|
-
{
|
|
254
|
-
"rotate-90": openSubMenu[menu.name],
|
|
255
|
-
}
|
|
256
|
-
)}
|
|
257
|
-
style={{ fontSize: "16px" }}
|
|
258
|
-
>
|
|
259
|
-
keyboard_arrow_right
|
|
260
|
-
</span>
|
|
261
|
-
</button>
|
|
262
|
-
) : null}
|
|
263
|
-
</div>
|
|
264
|
-
{menu.subItems ? (
|
|
265
|
-
<ul
|
|
266
|
-
className={clsx(
|
|
267
|
-
"block",
|
|
268
|
-
{
|
|
269
|
-
[`hidden absolute top-[42px] group-hover:block hover:block nav-bg nav-${navType} border nav-border`]:
|
|
270
|
-
type == "topmenu",
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
hidden: !openSubMenu[menu.name] && type == "sidebar",
|
|
274
|
-
}
|
|
275
|
-
)}
|
|
276
|
-
>
|
|
277
|
-
{menu.subItems.map((submenu) => {
|
|
278
|
-
return (
|
|
279
|
-
<li key={submenu.name}>
|
|
280
|
-
<Link
|
|
281
|
-
className={clsx(
|
|
282
|
-
`block nav-text-standard nav-${navType} nav-padding`,
|
|
283
|
-
{
|
|
284
|
-
"nav-text-active": submenu.path == pathname,
|
|
285
|
-
},
|
|
286
|
-
{ "pl-12 ": type == "sidebar" }
|
|
287
|
-
)}
|
|
288
|
-
href={submenu.path}
|
|
289
|
-
>
|
|
290
|
-
{submenu.name}
|
|
291
|
-
</Link>
|
|
292
|
-
</li>
|
|
293
|
-
);
|
|
294
|
-
})}
|
|
295
|
-
</ul>
|
|
296
|
-
) : null}
|
|
297
|
-
</li>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
})}
|
|
301
|
-
</ul>
|
|
302
|
-
</nav>
|
|
303
|
-
);
|
|
304
|
-
};
|
|
305
|
-
export default Navigation;
|
|
306
|
-
export { Sidebar, GlobalNav, NavToggleButton, NavItems };
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
Navigation,
|
|
163
|
+
NavBrand,
|
|
164
|
+
NavList,
|
|
165
|
+
NavItem,
|
|
166
|
+
NavLink,
|
|
167
|
+
NavTrigger,
|
|
168
|
+
NavContainer
|
|
169
|
+
}
|
|
@@ -1,69 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
border-color: var(--border-topnav);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.nav-container.nav-sidenav {
|
|
10
|
-
background-color: var(--bg-sidenav);
|
|
11
|
-
color: var(--text-sidenav-standard);
|
|
12
|
-
border-color: var(--border-sidenav);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.nav-bg.nav-topnav {
|
|
16
|
-
background-color: var(--bg-topnav);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.nav-bg.nav-sidenav {
|
|
20
|
-
background-color: var(--bg-sidenav);
|
|
1
|
+
.nav-menu{
|
|
2
|
+
background-color: var(--bg-card);
|
|
3
|
+
padding: 0 var(--spacing-common-x);
|
|
4
|
+
border-bottom: 1px solid var(--border-standard);
|
|
5
|
+
position: relative;
|
|
21
6
|
}
|
|
22
7
|
|
|
23
|
-
.nav-
|
|
24
|
-
|
|
8
|
+
.nav-item{
|
|
9
|
+
display: block;
|
|
10
|
+
border-radius: var(--radius-common);
|
|
11
|
+
padding: var(--spacing-common-y) 0;
|
|
12
|
+
color: var(--text-standard);
|
|
25
13
|
}
|
|
26
14
|
|
|
27
|
-
.nav-border.nav-sidenav {
|
|
28
|
-
border-color: var(--border-sidenav);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.nav-text-standard.nav-topnav {
|
|
32
|
-
color: var(--text-topnav-standard);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.nav-text-standard.nav-sidenav {
|
|
36
|
-
color: var(--text-sidenav-standard);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.nav-text-active.nav-topnav {
|
|
40
|
-
color: var(--text-topnav-active);
|
|
41
|
-
}
|
|
42
15
|
|
|
43
|
-
.nav-
|
|
44
|
-
|
|
16
|
+
.nav-link{
|
|
17
|
+
display: block;
|
|
18
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
19
|
+
border-radius: var(--radius-common);
|
|
20
|
+
cursor: pointer;
|
|
45
21
|
}
|
|
46
22
|
|
|
47
|
-
.nav-
|
|
48
|
-
color: var(--
|
|
23
|
+
.nav-link:hover{
|
|
24
|
+
background-color: var(--bg-hover);
|
|
49
25
|
}
|
|
50
26
|
|
|
51
|
-
.nav-
|
|
52
|
-
color: var(--
|
|
27
|
+
.nav-link.active{
|
|
28
|
+
color: var(--color-primary);
|
|
53
29
|
}
|
|
54
30
|
|
|
55
|
-
.nav-
|
|
56
|
-
|
|
31
|
+
.nav-container {
|
|
32
|
+
position: absolute;
|
|
33
|
+
padding: var(--spacing-common-y) 0;
|
|
34
|
+
top: 100%;
|
|
35
|
+
left: 0;
|
|
36
|
+
right: 0;
|
|
37
|
+
width: 100%;
|
|
38
|
+
z-index: 100;
|
|
57
39
|
}
|
|
58
40
|
|
|
59
|
-
.nav-
|
|
60
|
-
|
|
41
|
+
.nav-content {
|
|
42
|
+
display: flex;
|
|
43
|
+
width: 100%;
|
|
44
|
+
flex-wrap: wrap;
|
|
45
|
+
gap: var(--spacing-common-x);
|
|
46
|
+
background-color: var(--bg-card);
|
|
47
|
+
border: 1px solid var(--border-standard);
|
|
48
|
+
border-radius: var(--radius-common);
|
|
49
|
+
padding: var(--spacing-common-y) var(--spacing-common-x);
|
|
50
|
+
box-shadow: var(--shadow-standard);
|
|
61
51
|
}
|
|
62
52
|
|
|
63
|
-
.nav-
|
|
64
|
-
|
|
53
|
+
.nav-arrow {
|
|
54
|
+
transition: transform 0.2s ease;
|
|
65
55
|
}
|
|
66
56
|
|
|
67
|
-
.nav-
|
|
68
|
-
|
|
57
|
+
.nav-arrow.open {
|
|
58
|
+
transform: rotate(180deg);
|
|
69
59
|
}
|