diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 3b80344..5692173 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,4 +1,5 @@ import type { Preview } from "@storybook/react-vite"; +import "../src/index.css"; const preview: Preview = { parameters: { diff --git a/README.md b/README.md index 0e9dd1c..f70b260 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ --- -# **Tech Stack** +## Tech Stack
-### **Frontend** +### Frontend ![React](https://img.shields.io/badge/React-61DAFB?style=for-the-badge&logo=react&logoColor=000) ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=fff) @@ -35,13 +35,12 @@ ![Zustand](https://img.shields.io/badge/Zustand-000000?style=for-the-badge) ![TailwindCSS](https://img.shields.io/badge/TailwindCSS-06B6D4?style=for-the-badge&logo=tailwindcss&logoColor=fff) -### **Backend** +### Backend ![Spring Boot](https://img.shields.io/badge/Spring_Boot-6DB33F?style=for-the-badge&logo=springboot&logoColor=fff) ![MySQL](https://img.shields.io/badge/MySQL-00618A?style=for-the-badge&logo=mysql&logoColor=fff) -![Nginx](https://img.shields.io/badge/Nginx-009639?style=for-the-badge&logo=nginx&logoColor=fff) -### **Infra** +### Infra ![Oracle Cloud](https://img.shields.io/badge/Oracle_Cloud-F80000?style=for-the-badge&logo=oracle&logoColor=fff) ![Firebase](https://img.shields.io/badge/Firebase-FFCA28?style=for-the-badge&logo=firebase&logoColor=000) @@ -51,144 +50,20 @@ --- -# **빠른 시작** +## 빠른 시작 ```bash -# 개발 서버 실행 -pnpm dev - -# Storybook 실행 (포트 6006) -pnpm storybook - -# 프로덕션 빌드 -pnpm build - -# 린트 검사 -pnpm lint +pnpm install +pnpm dev # 개발 서버 +pnpm storybook # Storybook (포트 6006) +pnpm build # 프로덕션 빌드 +pnpm lint # 린트 검사 ``` ---- - -# **프로젝트 개요** - -AIM AJOU는 다음 기능을 목표로 한다: - -- **Firebase Auth 기반 사용자 인증** -- **프로젝트/활동 기록 관리** -- **AI 기반 포트폴리오 구성 추천 (향후 기능)** -- **클라우드 기반 프로젝트 데이터 저장 (Firestore, Storage)** -- **반응형 UI를 통한 학생 포트폴리오 웹뷰 제공** -- **GitHub Actions + Firebase Hosting 자동 배포 CI/CD** +> 환경 변수(.env) 설정이 필요합니다. 팀 노션을 참고하세요. --- -# **폴더 구조 (FSD Architecture)** - -이 프로젝트는 **Feature-Sliced Design (FSD)** 아키텍처를 따릅니다. - -``` -aim-frontend/ - ├─ .storybook/ # Storybook 설정 (main.ts, preview.ts) - ├─ public/ - ├─ src/ - │ ├─ app/ # 애플리케이션 초기화 - │ │ ├─ providers/ # Context providers - │ │ ├─ router/ # 라우팅 설정 - │ │ ├─ index.tsx # App 컴포넌트 - │ │ └─ index.css # 전역 스타일 - │ │ - │ ├─ pages/ # 페이지 레이어 - │ │ ├─ home/ # 홈 페이지 - │ │ └─ login/ # 로그인 페이지 - │ │ - │ ├─ widgets/ # 위젯 레이어 (큰 UI 블록) - │ │ └─ header/ # 헤더 위젯 - │ │ ├─ ui/ # UI 컴포넌트 - │ │ └─ index.ts # Public API - │ │ - │ ├─ features/ # 기능 레이어 - │ │ └─ auth/ # 인증 기능 - │ │ ├─ ui/ # UI 컴포넌트 - │ │ ├─ model/ # 비즈니스 로직 - │ │ ├─ api/ # API 요청 - │ │ └─ index.ts # Public API - │ │ - │ ├─ entities/ # 엔티티 레이어 - │ │ └─ user/ # 사용자 엔티티 - │ │ ├─ ui/ # UI 컴포넌트 - │ │ ├─ model/ # 데이터 모델 - │ │ ├─ api/ # API 요청 - │ │ └─ index.ts # Public API - │ │ - │ └─ shared/ # 🔧 공유 레이어 - │ ├─ ui/ # 재사용 가능한 UI 컴포넌트 - │ │ ├─ button/ # 버튼 컴포넌트 - │ │ └─ input/ # 인풋 컴포넌트 - │ ├─ assets/ # 전역 에셋 (images, icons, fonts) - │ ├─ config/ # 설정 (firebase.ts) - │ ├─ api/ # API 클라이언트 - │ ├─ lib/ # 유틸리티 함수 - │ └─ types/ # 공통 타입 정의 - │ - ├─ .github/workflows/ # PR Preview & Live Deploy CI/CD - ├─ .husky/ # pre-commit hooks - ├─ firebase.json - ├─ firestore.rules - ├─ storage.rules - ├─ eslint.config.js # ESLint Flat Config - ├─ prettier.config.cjs - ├─ tailwind.config.js - ├─ tsconfig.json - └─ package.json -``` - -### FSD 레이어 규칙 - -**상위 레이어 → 하위 레이어** 방향으로만 의존 가능: - -``` -app → pages → widgets → features → entities → shared -``` - -- **하위 레이어는 상위 레이어를 import할 수 없습니다** -- **`@/` alias를 사용하여 절대 경로로 import** -- **각 슬라이스는 public API(index.ts)를 통해 export** - ---- +## 개발 가이드 -# **API 통신 구조** - -Oracle VM → Nginx → Spring Boot → MySQL - -Frontend에서 모든 요청은 `src/lib/api.ts`에서 제공하는 Axios instance를 사용함. - -- 자동 Authorization 헤더 삽입 -- 에러 처리 인터셉터 -- Response 타입 안전성 확보 - ---- - -# **Git Hooks (Husky + lint-staged)** - -커밋 시 자동: - -- eslint --fix -- prettier --write - -→ 포맷팅되지 않은 코드는 커밋 불가 - ---- - -# **CI/CD – GitHub Actions + Firebase Hosting** - -### PR 생성 → - -- 빌드 검사 -- ESLint 검사 -- Firebase Preview URL 자동 생성 - -### main에 머지 → - -- Firebase Hosting Live 자동 배포 - ---- +프로젝트 구조, 컨벤션, 워크플로우는 **[CONTRIBUTING.md](./CONTRIBUTING.md)** 를 참고하세요. \ No newline at end of file diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..1c7ce03 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,382 @@ +# AIM AJOU — 프론트엔드 개발 가이드 + +## 목차 + +1. [개발 환경 설정](#개발-환경-설정) +2. [프로젝트 구조](#프로젝트-구조) +3. [아키텍처](#아키텍처) +4. [개발 워크플로우](#개발-워크플로우) +5. [코드 컨벤션](#코드-컨벤션) +6. [컴포넌트 개발 가이드](#컴포넌트-개발-가이드) +7. [상태 관리 및 데이터 패칭](#상태-관리-및-데이터-패칭) +8. [API 호출 가이드](#api-호출-가이드) +9. [의존성 관리](#의존성-관리) +10. [CI/CD](#cicd) + +--- + +## 개발 환경 설정 + +### 사전 요구사항 + +- **Node.js**: v20.x (v22 LTS 업그레이드 검토 중 — 현재 v20 사용) +- **pnpm**: 패키지 매니저 (의존성 관리를 위해 npm/yarn 사용 금지) + +### 설치 및 실행 + +```bash +# 의존성 설치 +pnpm install + +# 개발 서버 실행 +pnpm dev + +# Storybook 실행 (포트 6006) +pnpm storybook + +# 프로덕션 빌드 +pnpm build + +# 린트 검사 +pnpm lint +``` + +### 환경 변수 설정 (.env) + +Firebase 연동을 위해 `.env` 파일 설정이 필요합니다. + +> `.env` 파일은 절대 GitHub에 커밋하지 마세요. `.gitignore`에 등록되어 있습니다. + +환경 변수 값은 **팀 노션**을 참고해서 프로젝트 루트에 `.env` 파일을 생성하세요. + +``` +VITE_FIREBASE_API_KEY=... +VITE_FIREBASE_AUTH_DOMAIN=... +VITE_FIREBASE_PROJECT_ID=... +VITE_FIREBASE_STORAGE_BUCKET=... +VITE_FIREBASE_MESSAGING_SENDER_ID=... +VITE_FIREBASE_APP_ID=... +``` + +--- + +## 프로젝트 구조 + +``` +aim-frontend/ + ├─ .storybook/ # Storybook 설정 (main.ts, preview.ts) + ├─ public/ + ├─ src/ + │ ├─ app/ # 애플리케이션 초기화 + │ │ ├─ providers/ # Context providers + │ │ ├─ router/ # 라우팅 설정 + │ │ ├─ index.tsx # App 컴포넌트 + │ │ └─ index.css # 전역 스타일 + │ │ + │ ├─ pages/ # 페이지 레이어 + │ ├─ widgets/ # 위젯 레이어 (큰 UI 블록) + │ ├─ features/ # 기능 레이어 + │ ├─ entities/ # 엔티티 레이어 + │ └─ shared/ # 공유 레이어 + │ ├─ ui/ # 재사용 가능한 UI 컴포넌트 + │ ├─ assets/ # 전역 에셋 (images, icons, fonts) + │ ├─ config/ # 설정 (firebase.ts) + │ ├─ api/ # API 클라이언트 + │ ├─ lib/ # 유틸리티 함수 + │ └─ types/ # 공통 타입 정의 + │ + ├─ docs/ # 문서 + │ └─ VERSIONS.md # 의존성 버전 관리 + ├─ .github/workflows/ # PR Preview & Live Deploy CI/CD + ├─ .husky/ # pre-commit hooks + ├─ firebase.json + ├─ firestore.rules + ├─ storage.rules + ├─ eslint.config.js + ├─ prettier.config.cjs + ├─ tailwind.config.js + └─ tsconfig.json +``` + +--- + +## 아키텍처 + +### FSD (Feature-Sliced Design) + +레이어 의존 방향은 **단방향**입니다. 하위 레이어는 상위 레이어를 import할 수 없습니다. + +``` +app → pages → widgets → features → entities → shared +``` + +- `@/` alias를 사용해 절대 경로로 import +- 각 슬라이스는 `index.ts`(public API)를 통해서만 export + +```ts +// 올바른 import +import { Button } from '@/shared/ui/button' +import { AuthFeature } from '@/features/auth' + +// 잘못된 import (하위에서 상위 참조 금지) +// shared에서 features import 불가 +``` + +### 인프라 구조 + +현재는 Vite 기반 SPA이며, Next.js App Router 기반 SSR 전환이 검토 중입니다. + +``` +브라우저 + └─ Firebase Hosting (정적 파일 서빙) + └─ Spring 백엔드 API 호출 (Oracle Cloud Run) + └─ MySQL (Oracle Server) + +Firebase: Hosting, Firestore, Storage, Auth +``` + +--- + +## 개발 워크플로우 + +### 1. 이슈 생성 + +개발 시작 전 반드시 이슈를 먼저 생성합니다. 이슈 템플릿에 맞춰 작성하세요. + +### 2. 브랜치 생성 + +브랜치명은 자유롭게 지어도 됩니다. 피처 단위로 브랜치를 따서 작업합니다. + +```bash +git checkout -b feat/button-component +git checkout -b fix/header-layout +``` + +### 3. 개발 및 커밋 + +커밋 시 Husky + lint-staged가 자동으로 실행됩니다. + +- `eslint --fix` +- `prettier --write` + +포맷팅되지 않은 코드는 커밋이 불가합니다. + +### 4. PR 생성 + +`dev` 브랜치로 PR을 올립니다. PR 템플릿에 맞춰 작성하세요. + +- **1명 이상의 리뷰어 승인** 후 머지 가능 +- 리뷰어는 주로 **컨벤션 준수 여부**를 중점적으로 확인합니다 + +### 5. 코드 리뷰 + +- **CodeRabbit**: 푸시 단위로 자동 리뷰를 달아줍니다. 지적 사항은 반드시 확인하세요. +- **GitHub Copilot**: 필요할 때 직접 요청해서 사용합니다. + +--- + +## 코드 컨벤션 + +기본적으로 **Airbnb 스타일 가이드**를 따릅니다. + +### 네이밍 + +| 대상 | 규칙 | 예시 | +|---|---|---| +| 변수 / 함수 | camelCase | `isLoading`, `getVariantClasses` | +| 타입 / 컴포넌트 / 인터페이스 | PascalCase | `ButtonProps`, `ButtonVariant`, `Spinner` | +| 불린 | `is/has/should/can` 접두사 | `isIconOnly`, `hasError` | +| 유니언 상수 | literal union | `type ButtonVariant = "primary" \| "secondary"` | +| 파일 / 폴더 | 케밥 케이스 | `button.tsx`, `user-card.tsx`, `use-auth.ts` | +| 스토리 파일 | `.stories.tsx` | `button.stories.tsx` | +| 테스트 파일 | `.test.ts(x)` / `.spec.ts(x)` | `button.test.tsx` | + +### 타입 + +```ts +// Enum 대신 literal union + as const 사용 +type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" + +const BUTTON_VARIANTS = ["primary", "secondary", "ghost", "danger"] as const +type ButtonVariant = typeof BUTTON_VARIANTS[number] + +// Enum 사용 금지 +enum ButtonVariant { Primary = "primary" } +``` + +- 함수 반환 타입 명시 (특히 public API, hooks) +- `interface`보다 `type` 선호 (단, 확장이 필요한 경우 `interface` 사용 가능) + +### 임포트 순서 + +```ts +// 1. 외부 라이브러리 +import { useState } from 'react' +import { useQuery } from '@tanstack/react-query' + +// 2. 내부 alias (FSD 레이어 순서) +import { apiClient } from '@/shared/api' +import { UserEntity } from '@/entities/user' +import { AuthFeature } from '@/features/auth' + +// 3. 상대 경로 +import { getButtonClasses } from './button.utils' +import type { ButtonProps } from './button.types' +``` + +--- + +## 컴포넌트 개발 가이드 + +### 스타일 클래스 패턴 + +```ts +// 토큰 모음: *Classes +const baseClasses = "inline-flex items-center font-medium transition-colors" +const sizeClasses = { sm: "px-3 py-1.5 text-sm", md: "px-4 py-2 text-base" } +const variantClasses = { + primary: "bg-[color:var(--color-primary-800,#004a9c)] text-white", + secondary: "bg-transparent border border-[color:var(--color-primary-800,#004a9c)]", +} + +// 조립 함수: get*Classes() +const getButtonClasses = (variant: ButtonVariant, size: ButtonSize) => { + return [baseClasses, sizeClasses[size], variantClasses[variant]].join(" ") +} + +// 최종 클래스 변수: 명사형 +const buttonClasses = getButtonClasses(variant, size) +``` + +- Tailwind + CSS 변수는 fallback 포함 권장: `bg-[color:var(--color-primary-800,#004a9c)]` +- 전역 스타일 최소화 — 전역 스타일은 Storybook과 앱 양쪽에서 동일한 진입점 CSS를 사용 + +### Props 패턴 + +```ts +// Props 인터페이스: ComponentNameProps +interface ButtonProps { + variant?: ButtonVariant + size?: ButtonSize + isLoading?: boolean // 불린: is/has/should/can 접두사 + onClick?: () => void // 핸들러: on* 접두사 + children: React.ReactNode +} +``` + +### Storybook 작성 (필수) + +컴포넌트 개발이 완료되면 반드시 Storybook 스토리를 함께 작성해야 합니다. + +```ts +// button.stories.tsx +import type { Meta, StoryObj } from '@storybook/react' +import { Button } from './button' + +const meta: Meta = { + title: 'Shared/UI/Button', + component: Button, + tags: ['autodocs'], +} +export default meta + +type Story = StoryObj + +export const Primary: Story = { + args: { variant: 'primary', children: '버튼' }, +} + +export const Loading: Story = { + args: { variant: 'primary', isLoading: true, children: '로딩 중' }, +} +``` + +--- + +## 상태 관리 및 데이터 패칭 + +| 용도 | 도구 | +|---|---| +| 서버 상태 (API 데이터) | **React Query** (`@tanstack/react-query`) | +| 클라이언트 전역 상태 | **Zustand** | +| 라우팅 | **TanStack Router** | + +```ts +// React Query — 서버 데이터 패칭 +const { data, isLoading } = useQuery({ + queryKey: ['portfolios'], + queryFn: () => apiClient.get('/portfolios'), +}) + +// Zustand — 클라이언트 전역 상태 +const useAuthStore = create((set) => ({ + user: null, + setUser: (user) => set({ user }), +})) +``` + +--- + +## API 호출 가이드 + +모든 API 요청은 `src/shared/api`의 Axios 인스턴스를 사용합니다. + +- 자동 Authorization 헤더 삽입 +- 에러 처리 인터셉터 +- Response 타입 안전성 확보 + +### Next.js 도입 시 (검토 중) + +Next.js App Router 전환 후에는 보안을 위해 **서버 컴포넌트에서 API를 호출**합니다. 클라이언트에서 백엔드를 직접 호출하는 것을 최소화하고, 인터랙션이 필요한 컴포넌트에만 `"use client"`를 선언합니다. + +```tsx +// 서버 컴포넌트 — 데이터 패칭 +async function PortfolioPage() { + const data = await fetch('https://api/portfolios').then(r => r.json()) + return +} + +// 클라이언트 컴포넌트 — 인터랙션만 +"use client" +function LikeButton() { + const [liked, setLiked] = useState(false) + return +} +``` + +--- + +## 의존성 관리 + +패키지 매니저는 **pnpm**만 사용합니다. + +```bash +# 패키지 추가 +pnpm add [패키지명] +pnpm add -D [패키지명] # 개발 의존성 + +# 패키지 제거 +pnpm remove [패키지명] +``` + +### VERSIONS.md 갱신 규칙 + +의존성을 추가하거나 업데이트할 경우 반드시 `docs/VERSIONS.md`를 함께 업데이트합니다. + +- 현재 버전 현황 테이블 갱신 +- 업데이트 이력에 날짜 및 변경 내역 추가 +- 반려한 항목이 있으면 반려 사유도 기록 + +--- + +## CI/CD + +### PR 생성 시 + +- 빌드 검사 +- ESLint 검사 +- Firebase Preview URL 자동 생성 + +### `main` 머지 시 + +- Firebase Hosting Live 자동 배포 \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js index c2ddf74..f69c5d4 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,5 +1,6 @@ export default { plugins: { "@tailwindcss/postcss": {}, + autoprefixer: {}, }, }; diff --git a/src/app/index.css b/src/app/index.css index 9132316..700850b 100644 --- a/src/app/index.css +++ b/src/app/index.css @@ -1,5 +1,5 @@ -/* App-specific root styles are imported from src/index.css */ -/* All shared styles have been consolidated in src/index.css to avoid duplication */ +/* App-specific styles for src/app/index.tsx */ +/* Shared global styles/tokens remain in src/index.css */ #root { max-width: 1280px; @@ -7,3 +7,48 @@ padding: 2rem; text-align: center; } + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.card { + padding: 2em; +} + +.card button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + color: #ffffff; + cursor: pointer; + transition: border-color 0.25s; +} + +.card button:hover { + border-color: #646cff; +} + +.card button:focus, +.card button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.read-the-docs { + color: #888; +} diff --git a/src/index.css b/src/index.css index f91c1a4..2dea895 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,4 @@ + @import "tailwindcss"; /* ==================================== @@ -196,27 +197,6 @@ body { -moz-osx-font-smoothing: grayscale; } -/* ============================================ - Base Styles - ============================================ */ - -* { - box-sizing: border-box; -} - -body { - margin: 0; - font-family: var(--font-primary); - color: var(--text-primary); - background-color: var(--color-bg-primary); - line-height: var(--line-height-normal); - min-width: 320px; - min-height: 100vh; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - #root { width: 100%; min-height: 100vh; @@ -264,45 +244,3 @@ body { .shadow-lg { box-shadow: var(--shadow-lg); } - -/* ============================================ - Legacy Styles (for compatibility) - ============================================ */ - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} - -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} - -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/pages/home/home.css b/src/pages/home/home.css deleted file mode 100644 index 2c9a9e0..0000000 --- a/src/pages/home/home.css +++ /dev/null @@ -1,68 +0,0 @@ -.storybook-page { - margin: 0 auto; - padding: 48px 20px; - max-width: 600px; - color: #333; - font-size: 14px; - line-height: 24px; - font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -.storybook-page h2 { - display: inline-block; - vertical-align: top; - margin: 0 0 4px; - font-weight: 700; - font-size: 32px; - line-height: 1; -} - -.storybook-page p { - margin: 1em 0; -} - -.storybook-page a { - color: inherit; -} - -.storybook-page ul { - margin: 1em 0; - padding-left: 30px; -} - -.storybook-page li { - margin-bottom: 8px; -} - -.storybook-page .tip { - display: inline-block; - vertical-align: top; - margin-right: 10px; - border-radius: 1em; - background: #e7fdd8; - padding: 4px 12px; - color: #357a14; - font-weight: 700; - font-size: 11px; - line-height: 12px; -} - -.storybook-page .tip-wrapper { - margin-top: 40px; - margin-bottom: 40px; - font-size: 13px; - line-height: 20px; -} - -.storybook-page .tip-wrapper svg { - display: inline-block; - vertical-align: top; - margin-top: 3px; - margin-right: 4px; - width: 12px; - height: 12px; -} - -.storybook-page .tip-wrapper svg path { - fill: #1ea7fd; -} diff --git a/src/pages/home/home.tsx b/src/pages/home/home.tsx index 12e8699..5931b66 100644 --- a/src/pages/home/home.tsx +++ b/src/pages/home/home.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Header } from "@/widgets/header"; -import "./home.css"; type User = { name: string; @@ -19,8 +18,10 @@ export const HomePage: React.FC = () => { onCreateAccount={() => setUser({ name: "Jane Doe" })} /> -
-

Pages in Storybook

+
+

+ Pages in Storybook +

We recommend building UIs with a{" "} @@ -33,8 +34,8 @@ export const HomePage: React.FC = () => { needing to navigate to them in your app. Here are some handy patterns for managing page data in Storybook:

-
); diff --git a/src/shared/ui/button/button.css b/src/shared/ui/button/button.css deleted file mode 100644 index 7efe955..0000000 --- a/src/shared/ui/button/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - display: inline-block; - cursor: pointer; - border: 0; - border-radius: 3em; - font-weight: 700; - line-height: 1; - font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} -.storybook-button--primary { - background-color: #555ab9; - color: white; -} -.storybook-button--secondary { - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; - background-color: transparent; - color: #333; -} -.storybook-button--small { - padding: 10px 16px; - font-size: 12px; -} -.storybook-button--medium { - padding: 11px 20px; - font-size: 14px; -} -.storybook-button--large { - padding: 12px 24px; - font-size: 16px; -} diff --git a/src/widgets/header/ui/header.css b/src/widgets/header/ui/header.css deleted file mode 100644 index ad77492..0000000 --- a/src/widgets/header/ui/header.css +++ /dev/null @@ -1,32 +0,0 @@ -.storybook-header { - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 15px 20px; - font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -.storybook-header svg { - display: inline-block; - vertical-align: top; -} - -.storybook-header h1 { - display: inline-block; - vertical-align: top; - margin: 6px 0 6px 10px; - font-weight: 700; - font-size: 20px; - line-height: 1; -} - -.storybook-header button + button { - margin-left: 10px; -} - -.storybook-header .welcome { - margin-right: 10px; - color: #333; - font-size: 14px; -} diff --git a/src/widgets/header/ui/header.tsx b/src/widgets/header/ui/header.tsx index 31488b0..9c5fa90 100644 --- a/src/widgets/header/ui/header.tsx +++ b/src/widgets/header/ui/header.tsx @@ -1,5 +1,4 @@ import { Button } from "@/shared/ui/button/button"; -import "./header.css"; type User = { name: string; @@ -14,9 +13,15 @@ export interface HeaderProps { export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
-
+
- + -

Acme

+

+ Acme +

{user ? ( <> - + Welcome, {user.name}!