From cb86b726a8c9d211703feb92530c9fb79c9fe3d1 Mon Sep 17 00:00:00 2001 From: manNomi Date: Tue, 14 Jul 2026 12:48:36 +0900 Subject: [PATCH 1/6] =?UTF-8?q?chore:=20=EB=8D=B0=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=ED=86=B1=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/omd-design.mdc | 22 +++++++ .gitignore | 4 ++ .omd/init-context.json | 18 ++++++ .omd/preferences.md | 21 +++++++ .omd/sync.lock.json | 9 +++ AGENTS.md | 8 +++ CLAUDE.md | 11 ++++ DESIGN.md | 113 +++++++++++++++++++++++++++++++++++ 8 files changed, 206 insertions(+) create mode 100644 .cursor/rules/omd-design.mdc create mode 100644 .omd/init-context.json create mode 100644 .omd/preferences.md create mode 100644 .omd/sync.lock.json create mode 100644 DESIGN.md diff --git a/.cursor/rules/omd-design.mdc b/.cursor/rules/omd-design.mdc new file mode 100644 index 000000000..0470e1acd --- /dev/null +++ b/.cursor/rules/omd-design.mdc @@ -0,0 +1,22 @@ +--- +description: Authoritative brand & UI design system. Read DESIGN.md before UI work. +globs: + - "**/*.tsx" + - "**/*.jsx" + - "**/*.vue" + - "**/*.svelte" + - "**/*.css" + - "**/*.scss" + - "**/tailwind.config.*" + - "**/components/**" + - "**/app/**/page.*" +alwaysApply: false +--- + + +The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI. + +Pending preference corrections: `@.omd/preferences.md`. + +Precedence: DESIGN.md > preferences.md > framework defaults. + diff --git a/.gitignore b/.gitignore index 02e8e113c..0ec7f3d18 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,7 @@ docs/superpowers/ # Turborepo .turbo **/.turbo + +# Local AI design harness installation +/.agents/ +/.codex/ diff --git a/.omd/init-context.json b/.omd/init-context.json new file mode 100644 index 000000000..efab32a0c --- /dev/null +++ b/.omd/init-context.json @@ -0,0 +1,18 @@ +{ + "reference_id": "skyscanner", + "description": "Apply Skyscanner's desktop layout principles to every Solid Connection web POC page while preserving the existing color concept and component design.", + "mode": "inspired", + "requested_components": ["button", "input", "table", "card", "badge", "tabs", "dialog"], + "builder_url": "https://oh-my-design.kr/builder?step=preview&ref=skyscanner&cfg=c2t5c2Nhbm5lcnx8fHx8MHxidXR0b24saW5wdXQsdGFibGUsY2FyZCxiYWRnZSx0YWJzLGRpYWxvZw", + "delta_set": { + "axes": { + "density.shift": 0 + }, + "voiceHints": ["Preserve Solid Connection's existing Korean product voice"], + "matchedKeywords": ["desktop", "product", "search", "results", "filters"], + "warnings": [ + "Skyscanner color, typography, logo, imagery, and branded component styling are explicitly excluded" + ] + }, + "bootstrapped_at": "2026-07-14T03:31:28.000Z" +} diff --git a/.omd/preferences.md b/.omd/preferences.md new file mode 100644 index 000000000..2e69f1199 --- /dev/null +++ b/.omd/preferences.md @@ -0,0 +1,21 @@ +--- +schema: omd.preferences/v1 +design_md_hash_at_creation: +--- + +# Preference Log + +## 2026-07-14T03:31:28.000Z — preserve-solid-connection-visual-identity + +```omd-meta +id: pref_mrk3guqr_1401f1bd +timestamp: 2026-07-14T03:31:28.000Z +scope: visualTheme +signal: user-statement +confidence: explicit +status: pending +source_agent: codex +source_context: "feat/skyscanner-layout-poc" +``` + +Use Skyscanner only as a layout reference and preserve Solid Connection's existing color concept and component design. diff --git a/.omd/sync.lock.json b/.omd/sync.lock.json new file mode 100644 index 000000000..409860db4 --- /dev/null +++ b/.omd/sync.lock.json @@ -0,0 +1,9 @@ +{ + "design_md_hash": "25bcf48d0657", + "targets": { + "CLAUDE.md": "672475c77b07", + "AGENTS.md": "be71c8d154c9", + ".cursor/rules/omd-design.mdc": "f188a3882e27" + }, + "updated_at": "2026-07-14T03:31:28.000Z" +} diff --git a/AGENTS.md b/AGENTS.md index 2ec944c94..fea962acb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,3 +51,11 @@ - Team workflow: `docs/development-workflow.md` and `CLAUDE.md`. - LLM repository context: `llms.txt`. - Bruno + codegen + React Query skill doc: `docs/skills/ai-bruno-codegen-react-query-skill.md`. + + +## Design System (oh-my-design) + +**Before any UI, styling, copy, or motion change, open and read `./DESIGN.md` in full.** It is the authoritative brand/design spec. Treat its tokens, voice, and component rules as binding unless the user overrides in chat. + +If present, read `./.omd/preferences.md` — pending corrections not yet folded into DESIGN.md. Apply them; flag conflicts. + diff --git a/CLAUDE.md b/CLAUDE.md index 7206666b2..e375e2ae9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,3 +55,14 @@ git commit -m "사용자 인증 기능을 추가했습니다. 이 기능은 JWT - 관련된 설정 변경 여러 작업을 했다면 각각 별도의 커밋으로 분리합니다. + + +# Design System (oh-my-design) + +The authoritative brand & UI spec is **@./DESIGN.md**. +Read before any UI/styling/microcopy/motion work. + +Preference log (pending corrections): @./.omd/preferences.md + +Precedence: DESIGN.md > preferences.md > your defaults. + diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 000000000..6944ca9a1 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,113 @@ +--- +omd: 0.1 +brand: Solid Connection +bootstrapped_from: skyscanner +bootstrapped_at: 2026-07-14T03:31:28.000Z +--- + +# Solid Connection Desktop Layout System + +This specification uses Skyscanner Backpack as a structural reference only. Solid Connection's visual identity is locked: existing colors, Pretendard typography, icons, copy style, component surfaces, and data behavior remain authoritative. + +## 1. Visual Theme + +- Keep the current calm, practical exchange-student product aesthetic. +- Do not copy Skyscanner colors, fonts, logo, illustrations, travel photography, or branded copy. +- Use Skyscanner only for desktop composition: a horizontal product navigation, a prominent task-entry area, centered wide content, filters beside results, and clear result density. +- Prefer functional product layouts over marketing-style hero sections. + +## 2. Color Tokens + +Colors are inherited from `apps/web/tailwind.config.ts` and must not be replaced by Skyscanner tokens. + +| Role | Token | Value | +|---|---|---| +| Primary action | `primary` | `#5950F6` | +| Secondary action | `secondary` | `#4672EE` | +| Primary tint | `primary-100` | `#EFEEFF` | +| Secondary tint | `secondary-100` | `#E8EDFD` | +| Canvas | `k-0` | `#FFFFFF` | +| Page background | `k-50` | `#F5F5F5` | +| Border | `k-100` | `#DDDDDF` | +| Primary text | `k-900` | `#1A1F27` | +| Secondary text | `k-500` | `#76797D` | + +Existing semantic accent colors remain allowed in their current domain-specific components. Do not introduce `#0062e3`, `#05203c`, Skyscanner teal, or Skyscanner error colors. + +## 3. Typography + +- Use the existing Pretendard variable font everywhere. +- Keep the existing `typo-*` utilities and their current values. +- Page title: `typo-bold-1` unless an existing page intentionally uses a smaller hierarchy. +- Section title: `typo-bold-4` or the existing local token. +- Body and metadata: preserve current `typo-medium-*` / `typo-regular-*` choices. +- Do not add Skyscanner Relative, Larken, viewport-scaled type, or negative letter spacing. + +## 4. Desktop Layout + +- Desktop begins at the repository's existing `md` breakpoint. +- Use one horizontal product navigation at the top. Keep it visible while scrolling. +- Center page content in a `1280px` maximum-width stage with `32-40px` side padding. +- Use a full-width task-entry band near the top of workflow-heavy pages. +- Results pages use a primary content column plus a `300-380px` filter/summary rail where useful. +- Dense card collections use responsive 2-4 column grids; long textual content remains a readable single column. +- Sticky rails sit below the top navigation, never underneath it. +- Auth and focused forms use a centered two-column composition only when the supporting panel adds real context. + +## 5. Mobile Layout + +- Preserve the current mobile UI and bottom navigation. +- Do not force desktop grids, horizontal product navigation, or wide containers onto mobile. +- Mobile remains single-column and keeps existing route-specific navigation and fixed actions. + +## 6. Components + +The builder-selected components are `button`, `input`, `table`, `card`, `badge`, `tabs`, and `dialog`. Their visual language remains the current Solid Connection implementation. + +- Buttons: current primary/secondary colors, existing radius, existing heights, and current disabled behavior. +- Inputs: current border, radius, focus behavior, and error treatment. +- Cards: white surface, `k-100` border, existing 8px radius, subtle current shadows only. +- Badges and tabs: current primary tint/primary text treatment; do not import Skyscanner pill or navy-chip styling. +- Tables and rows: prioritize scanability and stable columns; preserve current semantic colors. +- Dialogs: preserve existing modal components and interaction behavior. + +## 7. Navigation + +- Desktop uses a white top bar with the Solid Connection cloud mark and wordmark. +- Primary sections remain 학교, 커뮤니티, 홈, 멘토, 마이. +- Active navigation uses existing primary colors and a subtle tinted surface. +- Mobile keeps the existing bottom navigation unchanged. + +## 8. Responsive Behavior + +| Width | Behavior | +|---|---| +| `<768px` | Existing mobile single-column UI | +| `768-1023px` | Compact desktop navigation, mostly single-column content | +| `1024-1279px` | Two-column workflows and wider card grids | +| `>=1280px` | Centered `1280px` stage, full result/filter composition | + +- Minimum interactive target remains 44px where the current component supports it. +- Long Korean labels must wrap or truncate deliberately; they must not overlap adjacent content. +- No horizontal page overflow at 390px, 1024px, or 1440px. + +## 9. States + +- Preserve existing loading, empty, error, disabled, and selected states. +- Skeleton dimensions should match final card dimensions to avoid layout shift. +- Empty and error states stay inside the same desktop stage as successful content. +- Do not replace domain-specific messages with Skyscanner copy. + +## 10. Motion + +- Preserve current restrained transitions. +- Hover elevation and translation should remain subtle and must not shift surrounding layout. +- Honor `prefers-reduced-motion` for newly introduced motion. +- Do not add decorative motion, carousel movement, or Skyscanner signature animations. + +## 11. Implementation Guardrails + +- Layout changes may reorganize existing components but must not change API calls, route behavior, or business rules. +- Reuse existing components and Tailwind tokens before introducing new abstractions. +- A page is complete only after desktop and mobile browser verification, overflow checks, broken-image checks, and console review. +- Skyscanner is an inspiration source, not a visual clone. The final screen must still be immediately recognizable as Solid Connection. From 25de9982779feb3a8b732c9d35a6a9303d873dff Mon Sep 17 00:00:00 2001 From: manNomi Date: Tue, 14 Jul 2026 12:49:02 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=A0=84=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=8D=B0=EC=8A=A4=ED=81=AC=ED=86=B1=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeDesktopView/HomeDesktopEntryPanel.tsx | 4 +- .../app/(home)/_ui/HomeDesktopView/index.tsx | 260 +++++++++--------- .../[boardCode]/CommunityPageContent.tsx | 4 +- .../community/[boardCode]/PostWriteButton.tsx | 2 +- .../[boardCode]/[postId]/CommentInput.tsx | 2 +- .../[boardCode]/[postId]/PostPageContent.tsx | 2 +- .../[postId]/modify/PostModifyForm.tsx | 4 +- .../community/[boardCode]/create/PostForm.tsx | 4 +- apps/web/src/app/login/LoginContent.tsx | 2 +- .../[id]/_ui/MentorDetialContent/index.tsx | 8 +- .../_ui/MentorClient/_ui/MenteePage/index.tsx | 2 +- .../_ui/MentorClient/_ui/MentorPage/index.tsx | 2 +- .../mentor/chat/_ui/ChatPageClient/index.tsx | 6 +- .../mentor/modify/_ui/ModifyContent/index.tsx | 4 +- .../waiting/_ui/WaitingContent/index.tsx | 2 +- .../src/app/my/_ui/MyProfileContent/index.tsx | 4 +- .../_components/CompletionScreen/index.tsx | 2 +- .../InterestCountriesScreen/index.tsx | 2 +- .../_components/StudyStatusScreen/index.tsx | 2 +- .../_components/UniversityScreen/index.tsx | 2 +- apps/web/src/app/my/apply-mentor/page.tsx | 4 +- .../my/favorite/_ui/FavoriteContent/index.tsx | 2 +- .../app/my/match/_ui/MatchContent/index.tsx | 2 +- .../app/my/modify/_ui/ModifyContent/index.tsx | 4 +- .../my/password/_ui/PasswordContent/index.tsx | 6 +- .../SchoolEmailVerificationContent/index.tsx | 6 +- .../src/app/sign-up/email/EmailSignUpForm.tsx | 4 +- apps/web/src/app/terms/page.tsx | 4 +- .../application/ScorePageContent.tsx | 4 +- .../ApplicationUniversityDetailContent.tsx | 122 +++++++- .../ApplicationBottomActionBar.tsx | 2 +- .../application/apply/ApplyPageContent.tsx | 4 +- .../src/app/university/score/ScoreScreen.tsx | 6 +- .../score/example/gpa-cert/page.tsx | 4 +- .../score/example/lang-cert/page.tsx | 4 +- .../score/submit/gpa/GpaSubmitForm.tsx | 4 +- .../language-test/LanguageTestSubmitForm.tsx | 4 +- .../components/layout/GlobalLayout/index.tsx | 4 +- .../ui/DesktopNavigation/index.tsx | 84 +++--- .../components/login/signup/SignupSurvey.tsx | 2 +- .../web/src/components/score/SubmitResult.tsx | 2 +- apps/web/src/styles/globals.css | 8 + 42 files changed, 356 insertions(+), 250 deletions(-) diff --git a/apps/web/src/app/(home)/_ui/HomeDesktopView/HomeDesktopEntryPanel.tsx b/apps/web/src/app/(home)/_ui/HomeDesktopView/HomeDesktopEntryPanel.tsx index a08d453fe..542eec3f5 100644 --- a/apps/web/src/app/(home)/_ui/HomeDesktopView/HomeDesktopEntryPanel.tsx +++ b/apps/web/src/app/(home)/_ui/HomeDesktopView/HomeDesktopEntryPanel.tsx @@ -81,12 +81,12 @@ const HomeDesktopEntryPanel = () => {

자주 쓰는 교환학생 업무를 바로 이어갈 수 있어요.

-
+
{actionLinks.map(({ href, label, description, icon: Icon, colorClassName, isUniversityZone }) => { const content = (
{label} diff --git a/apps/web/src/app/(home)/_ui/HomeDesktopView/index.tsx b/apps/web/src/app/(home)/_ui/HomeDesktopView/index.tsx index ec5a308e4..6203b658b 100644 --- a/apps/web/src/app/(home)/_ui/HomeDesktopView/index.tsx +++ b/apps/web/src/app/(home)/_ui/HomeDesktopView/index.tsx @@ -51,7 +51,7 @@ const HomeDesktopView = ({ recommendedUniversities, allRegionsUniversityList, ne })); return ( -
+

Solid Connection

@@ -70,141 +70,139 @@ const HomeDesktopView = ({ recommendedUniversities, allRegionsUniversityList, ne
-
- - -
-
- -
- {popularUniversities.map((university, index) => ( - -
- {`${university.koreanName} -
- - {university.country} - -
-
-

- {university.koreanName} -

-

{university.region}

-

{getLanguageRequirementText(university)}

-
- - ))} +
+ 국가 + {countryCount}
-
- -
- -
- {previewUniversities.map((university) => ( - +
+
+ {universityCountsByHome.map((homeUniversity) => ( + + {homeUniversity.name} + {homeUniversity.count}개 + + ))} +
+
+ + +
+
+ +
+ {popularUniversities.map((university, index) => ( + +
{`${university.koreanName} -
-

- {university.koreanName} -

-

- {university.country} · {university.region} -

-

모집 {university.studentCapacity}명

-
- - ))} -
-
- -
-
-
-

솔커에서 맛보는 소식

- -
+
+ + {university.country} + +
+
+

+ {university.koreanName} +

+

{university.region}

+

{getLanguageRequirementText(university)}

+
+ + ))} +
+
+ +
+ +
+ {previewUniversities.map((university) => ( + + {`${university.koreanName} +
+

+ {university.koreanName} +

+

+ {university.country} · {university.region} +

+

모집 {university.studentCapacity}명

+
+
+ ))} +
+
+ +
+
+
+

솔커에서 맛보는 소식

+
-
- {newsList.map((news) => ( - -
- {news.title} -
-
-

{news.title}

-

{news.description}

-
- - ))} -
-
-
-
+
+
+ {newsList.map((news) => ( + +
+ {news.title} +
+
+

{news.title}

+

{news.description}

+
+ + ))} +
+ +
); }; diff --git a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx index d69dce20e..59d469a9c 100644 --- a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx +++ b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx @@ -151,7 +151,7 @@ const CommunityDesktopView = ({ postWriteHandler, }: CommunityViewProps) => { return ( -
+

Community

@@ -170,7 +170,7 @@ const CommunityDesktopView = ({
-