diff --git a/DesignGuidelines.html b/DesignGuidelines.html new file mode 100644 index 0000000..70c2892 --- /dev/null +++ b/DesignGuidelines.html @@ -0,0 +1,4142 @@ + + + + + + Pokémon UI Library Design Guidelines v3 + + + + + + + +
+
+
+ Authorized Pokémon Product System +

Pokémon UI Library Design Guidelines v3

+

+ 针对已获得授权的 Pokémon 网页、活动页、会员中心、图鉴工具和电商体验,建立一套可落地的 UI 库。v3 在基础控件之上补齐 Team Builder、Evolution Chain、Move Data、Weakness Matrix、Battle Menu、Bag、Event 和 Reward 等产品专属组件。 +

+ + +
+ + +
+ +
+
+
+ 01 / Creative Direction +

设计方向

+

旧版的问题是过度抽象成泛怪兽风。新版以授权 Pokémon 产品为前提,明确使用官方视觉元素,但把它们工程化为可复用的 UI 语言。

+
+ +
+
+ 01 +

Official First

+

可使用 Pokémon、Poké Ball、Pokédex、官方角色名、类型体系、HP/EXP、Gym Badge 等授权资产语言。

+
+
+ 02 +

Game UI, Web Quality

+

借用游戏中的状态、收集、队伍、战斗和背包结构,但网页控件必须保持可访问、可扫描、可响应。

+
+
+ 03 +

Bright, Not Noisy

+

黄色、蓝色、红色用于关键动作和品牌识别;大面积阅读区域仍使用白色、浅蓝灰和明确分割线。

+
+
+ 04 +

System Completeness

+

从按钮、表单、筛选、Tab、弹窗、Toast 到图鉴卡、数据表、页面模板,全部使用同一套 tokens。

+
+
+
+
+ +
+
+
+ 02 / Official Elements +

官方元素使用规范

+

授权场景下可以使用官方元素,但需要把“品牌资产”和“UI 控件”分层管理。Logo 和角色图优先作为内容资产,Poké Ball、类型色、徽章和图鉴框架可以进入组件体系。

+
+ +
+
+ Pokémon +
+
T
+
+ TRAINER PASS + Ash Ketchum + Pallet Town · Badge 08 +
+
+
+ +
+
+ + Poké Ball + 用于图标、空状态、加载、徽章背景 +
+
+ + Gym Badge + 用于等级、成就、会员权益 +
+
+ Fire + Type System + 筛选、标签、属性、状态分类 +
+
+ DEX #150 + Pokédex ID + 列表编号、详情标识、收藏序号 +
+
+
+ +
+
+

可进入 UI 库的元素

+
    +
  • Poké Ball 作为系统图标、加载器、选择状态、空状态主符号。
  • +
  • 使用 frontend/public/types/ 中的 Type 图片资产作为筛选、标签、数值、卡片边缘和图表标识;CSS 类型色保留为 token 和兜底。
  • +
  • Pokédex 屏幕、训练家卡、Gym Badge、HP/EXP Bar 作为组件构型。
  • +
  • 官方角色名和编号可用于样例数据,如 Pikachu #025、Charizard #006、Eevee #133。
  • +
+
+
+

仍需控制的元素

+
    +
  • Logo 不作为普通按钮图标反复出现,只用于品牌区域、授权说明和首屏识别。
  • +
  • 角色官方插画不应被裁切到无法识别,也不应被当作装饰纹理平铺。
  • +
  • 高饱和类型色必须配合文字对比策略,Electric、Ice、Ground、Steel 等浅色标签用深色字。
  • +
  • 战斗 UI 可以借鉴状态结构,不建议直接把主机游戏画面一比一搬到网页任务流。
  • +
+
+
+
+
+ +
+
+
+ 03 / Design Tokens +

视觉令牌

+

Tokens 要覆盖品牌色、类型色、基础表面、描边、阴影、圆角和组件状态。这里采用 8px 卡片圆角,强调专业网页质感,而不是过度玩具化。

+
+ +
+
Pokémon YellowPrimary CTA / Logo fill#FFCB05
+
Pokémon BlueNavigation / Links#2A75BB
+
Deep BlueHeader / Text contrast#003A70
+
Poké RedUrgent / Ball top#EE1515
+
Ball BlackIcon stroke / Strong line#202124
+
Map MistPage background#F2F5FA
+
+ +
+
+

Type Badge Assets

+ large badges + small icons +
+
+ Normal + Fighting + Flying + Poison + Ground + Rock + Bug + Ghost + Steel + Fire + Water + Grass + Electric + Psychic + Ice + Dragon + Dark + Fairy + Stellar +
+

+ 横向徽章使用 frontend/public/types/1.pngfrontend/public/types/19.png;紧凑网格、筛选器、矩阵和卡片内图标使用 frontend/public/types/small/1.pngfrontend/public/types/small/18.png。文件映射:1 Normal, 2 Fighting, 3 Flying, 4 Poison, 5 Ground, 6 Rock, 7 Bug, 8 Ghost, 9 Steel, 10 Fire, 11 Water, 12 Grass, 13 Electric, 14 Psychic, 15 Ice, 16 Dragon, 17 Dark, 18 Fairy, 19 Stellar. +

+
+ +
+
+

Typography

+

标题使用圆润粗体,正文使用系统无衬线。Pokémon wordmark 只用于品牌展示,不替代正文或按钮文字。

+
+

Catch, Train, Explore

+

正文保持 16px 以上,行高 1.55 至 1.7,保证儿童和成人用户都能快速阅读。

+ CAPTION / DEX ENTRY +
+
+
+

Shape

+

卡片半径 8px,控件半径 8px,Pill 只用于 type chips、HP、status badges。Poké Ball 和头像保持圆形。

+
+ + Water + +
+
+
+

Elevation

+

主 CTA 和游戏化面板可使用硬阴影,普通信息卡使用柔和阴影。阴影不可替代清晰边框。

+
+ +
Soft rowReady
+
+
+
+
+
+ +
+
+
+ 04 / Controls +

控件库

+

控件覆盖按钮、图标按钮、输入、选择、开关、范围、步进器、分段控制、Tabs 和筛选标签。所有控件最小点击高度不低于 44px。

+
+ +
+
+

Buttons

Default / Hover / Active / Disabled
+
+
+ + + + + +
+
+ + + + + +
+
+
+ +
+

Inputs

A11y ready
+
+
+ + +
+
+
+ + + 可用名称 +
+
+ + +
+
+
+ +
+ ID + + GEN 1 +
+ 需要 12 位数字代码。 +
+
+ + +
+
+
+ +
+

Selection Controls

+
+
+ + +
+
+ + + +
+
+ + +
+
+ Capture chance +
+ + 64 +
+
+
+ Potion count +
+ + 3 + +
+
+
+
+ +
+

Segmented / Tabs / Chips

+
+
+ + + +
+ +
+
+ + + +
+
+

Base tab 显示编号、分类、身高、体重、能力和基础数值。

+
+
+

Moves tab 显示 Thunderbolt、Quick Attack、Iron Tail 等技能列表。

+
+
+

Evolution tab 显示 Pichu → Pikachu → Raichu 的进化路径。

+
+
+ +
+ Electric + Fire + Water + Grass +
+
+
+
+
+
+ +
+
+
+ 05 / Data Display +

数据展示组件

+

Pokémon 产品通常需要承载大量收集、列表、数值和状态。展示组件应优先保证对比和扫描效率,再加入品牌趣味。

+
+ +
+
+
+ +
+
+
+
+ #025 + Pikachu +
+ HP 35 +
+
+ Electric +
+
+
EXP68%
+
+
+
+
+ +
+

List Rows

+
+
+ +
Charizard

Fire / Flying · #006

+ Rare +
+
+ +
Squirtle

Water · #007

+ Caught +
+
+ +
Mewtwo

Psychic · #150

+ Locked +
+
+
+ +
+

Stats / Progress

+
+
+
HP82 / 100
+
+
+
+
EXP44%
+
+
+
+
Catch rate18%
+
+
+
+
Snorlax Lv. 42HP
+
+
+
+
+
+ +
+

Data Table

Sortable / Filterable
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DexNameTypeRegionStatusAction
#001BulbasaurGrassKantoCaught
#004CharmanderFireKantoSeen
#007SquirtleWaterKantoCaught
+
+
+
+
+ +
+
+
+ 06 / Pokémon Product Components +

Pokémon 专属组件

+

v3 新增一组真正贴近 Pokémon 产品场景的组件:搜索联想、进阶 Type 筛选、进化链、技能数据、弱点矩阵、队伍构建、对比面板、战斗菜单、背包、活动和奖励体系。

+
+ +
+
+
+

Pokémon Autocomplete

+ Combobox +
+
+
+ + +
    +
  • + +
    Pikachu#025 · Mouse Pokémon
    + Electric +
  • + +
  • + +
    Pichu#172 · Tiny Mouse Pokémon
    + Electric +
  • + +
+
+

用于 Pokédex、Team Builder、Shop search。结果项建议包含编号、名称、分类和 Type badge。

+
+
+ +
+
+

Advanced Type Filter

+ Multi-select +
+
+
+
+ Selected: Electric, Flying +

支持多选、清空、组合筛选和弱点模式。

+
+ +
+
+ + + + + + + + +
+
+
+ +
+
+

Evolution Chain

+ Detail page +
+
+
+
172
+ Pichu + Electric +

High friendship

+
+
+
025
+ Pikachu + Electric +

Thunder Stone

+
+
+
026
+ Raichu + Electric +

Final form

+
+
+
+ +
+
+

Move Cards

+ Power / Accuracy / PP +
+
+
+
ThunderboltElectric
+

May leave the target with paralysis.

+
SpecialPower 90Acc 100PP 15
+
+
+
Quick AttackNormal
+

This move always goes first.

+
PhysicalPower 40Acc 100PP 30
+
+
+
Iron TailSteel
+

May lower the target's Defense stat.

+
PhysicalPower 100Acc 75PP 15
+
+
+
Electro BallElectric
+

The faster the user, the greater the power.

+
SpecialVariableAcc 100PP 10
+
+
+
+
+ +
+
+
+

Weakness Matrix

+ Electric / Flying sample +
+
+
Ground2x
+
Rock2x
+
Ice2x
+
Fighting0.5x
+
Flying0.5x
+
Steel0.5x
+
Bug0.5x
+
Grass0.5x
+
Electric0.5x
+
Ghost1x
+
Dragon1x
+
Fairy1x
+
+
+ +
+
+

Team Builder Slots

+ 6 slots +
+
+ + + + + + +
+
+
+ +
+
+

Compare Panel

3-way compare
+
+
MetricPikachuRaichuJolteon
+
TypeElectricElectricElectric
+
HP356065
+
Attack559065
+
Speed90110130
+
AbilityStaticStaticVolt Absorb
+
+
+ +
+

Base Stats Radar

SVG tokenized
+
+ + + + + + + + + + + + HP + ATK + DEF + SPD + SP.DEF + SP.ATK + +

雷达图适合详情页摘要;精确对比仍建议使用条形图或 Compare Panel。

+
+
+
+ +
+
+

Battle Action Menu

+
+ + + + +
+
+ +
+

Bag Item Card

+
+
+ +
Hyper Potion

Restores 120 HP to one Pokémon.

Qty 12
+
+
+ +
Ultra Ball

A high-performance Ball with a better catch rate.

Qty 4
+
+
+
+ +
+

Event / Reward

+
+
+
+ Electric Spotlight Weekend +

Featured Pokémon, bonus candy, limited raid tasks.

+
+ Electric + Open +
+
+ 18h +
+
+
1Badge
+
Potion
+
Ball
+
?Locked
+
Secret
+
+
+
+
+
+
+ +
+
+
+ 07 / Feedback & Overlays +

反馈与覆盖层

+

反馈组件需要清晰表达系统状态。Poké Ball 可以作为提示图标,但错误、成功、等待、解锁仍要配合文字和颜色。

+
+ +
+
+

Alerts

+
+
+ +
Pokémon caught!

Pikachu has been added to your Pokédex.

+ +
+
+ ! +
Bag almost full

You have 2 slots left. Transfer items before the next battle.

+ +
+
+ × +
Connection lost

Battle data will retry automatically.

+ +
+
+
+ +
+

Modal / Toast / Tooltip

+
+
+ + + + + 捕获率提示需要解释公式来源和影响因素。 + +
+
+ +
+

No Pokémon found

+

调整类型筛选或搜索其它区域。

+
+ +
+
+
+ +
+

Loading States

+
+
+
+
+
+
+
+
+
+ +
+

Motion Rules

+
    +
  • 按钮 hover 上浮 2px,active 下压 2px,时长 120ms 至 180ms。
  • +
  • 捕获、升级、徽章解锁可使用 300ms 左右的弹性动效。
  • +
  • 列表筛选和 Tab 切换不使用大幅移动,避免影响扫描。
  • +
  • 必须支持 prefers-reduced-motion,关闭闪烁和持续动画。
  • +
+
+
+
+
+ + + +
+
+
+ 09 / Templates +

页面模板

+

以下模板把控件组合成可直接落地的页面结构,覆盖图鉴列表、详情页、战斗状态和背包网格。

+
+ +
+
+
+ Pokédex Listing + +
+
+
+ + +
+
+ Electric + Fire + Water + Grass +
+
+
#025 PikachuElectric
+
#006 CharizardFire
+
#007 SquirtleWater
+
#133 EeveeNormal
+
#143 SnorlaxNormal
+
#150 MewtwoPsychic
+
+
+
+ +
+
+
+ Pokémon Detail + #025 +
+
+
+
+
+
+

Pikachu

+

Mouse Pokémon · Electric

+
+
+ Electric + HP 35 +
+
+
Friendship84%
+
+
+ +
+
+
+
+ +
+
+ Battle & Bag + Live +
+
+
+
Raichu Lv. 36HP 62/90
+
+
+
+
Poké Ball
+
Potion
+
Rare Candy
+
Badge
+
+
+
+
+
+
+
+ +
+
+
+ 10 / Delivery Guidelines +

交付规范

+

设计稿和前端实现需要以 token 与官方资产目录为唯一来源,避免组件各自写死颜色和尺寸。Logo、角色图、商品图与 Type 图片必须有授权来源和版本记录。

+
+ +
+
+

设计验收清单

+
    +
  • 所有页面使用同一套 Pokémon brand tokens、type assets、type tokens、surface tokens。
  • +
  • Logo、角色图、官方插画、商品图均标注授权来源和使用范围。
  • +
  • 核心控件覆盖 default、hover、active、focus、disabled、loading、error 状态。
  • +
  • 图鉴列表、详情、Team Builder、Move Data、Weakness Matrix、背包、战斗状态、活动卡片均有桌面和移动端布局。
  • +
  • Type 展示优先使用 frontend/public/types/ 图片资产,隐藏文本和 CSS 类型色作为语义与兜底。
  • +
+
+ +
+

前端验收清单

+
    +
  • CSS 变量集中声明,不在组件内硬编码品牌色和类型色。
  • +
  • 控件高度不低于 44px,键盘焦点清晰,弹窗有 focus 管理和 ESC 关闭。
  • +
  • Tab、Modal、Toast、Switch、Range、Combobox、Type Filter、Team Slot 等控件具备基础 ARIA 语义。
  • +
  • 支持明暗主题与 reduced motion,动画不会阻塞主要任务流程。
  • +
  • 表格、列表、图鉴网格在 320px 宽度下不产生内容重叠。
  • +
+
+
+ +
+
:root {
+  --pokemon-yellow: #ffcb05;
+  --pokemon-blue: #2a75bb;
+  --pokemon-blue-deep: #003a70;
+  --pokemon-red: #ee1515;
+  --line-strong: #1f2a3b;
+  --radius-card: 8px;
+  --radius-control: 8px;
+  --shadow-control: 0 3px 0 var(--line-strong);
+}
+
+.btn.primary {
+  min-height: 44px;
+  border: 2px solid var(--line-strong);
+  border-radius: var(--radius-control);
+  background: var(--pokemon-yellow);
+  color: #172036;
+  box-shadow: var(--shadow-control);
+}
+
+
+
+
+ + + +
+
+ +
+ Item added +

Potion has been added to your Bag.

+
+
+
+ + + + + + diff --git a/frontend/public/types/1.png b/frontend/public/types/1.png new file mode 100644 index 0000000..bf9f3d1 Binary files /dev/null and b/frontend/public/types/1.png differ diff --git a/frontend/public/types/10.png b/frontend/public/types/10.png new file mode 100644 index 0000000..3add23c Binary files /dev/null and b/frontend/public/types/10.png differ diff --git a/frontend/public/types/11.png b/frontend/public/types/11.png new file mode 100644 index 0000000..a35f2ef Binary files /dev/null and b/frontend/public/types/11.png differ diff --git a/frontend/public/types/12.png b/frontend/public/types/12.png new file mode 100644 index 0000000..893e8ce Binary files /dev/null and b/frontend/public/types/12.png differ diff --git a/frontend/public/types/13.png b/frontend/public/types/13.png new file mode 100644 index 0000000..e555254 Binary files /dev/null and b/frontend/public/types/13.png differ diff --git a/frontend/public/types/14.png b/frontend/public/types/14.png new file mode 100644 index 0000000..8839cce Binary files /dev/null and b/frontend/public/types/14.png differ diff --git a/frontend/public/types/15.png b/frontend/public/types/15.png new file mode 100644 index 0000000..d65f954 Binary files /dev/null and b/frontend/public/types/15.png differ diff --git a/frontend/public/types/16.png b/frontend/public/types/16.png new file mode 100644 index 0000000..65b05ff Binary files /dev/null and b/frontend/public/types/16.png differ diff --git a/frontend/public/types/17.png b/frontend/public/types/17.png new file mode 100644 index 0000000..ae8dee2 Binary files /dev/null and b/frontend/public/types/17.png differ diff --git a/frontend/public/types/18.png b/frontend/public/types/18.png new file mode 100644 index 0000000..55e510b Binary files /dev/null and b/frontend/public/types/18.png differ diff --git a/frontend/public/types/19.png b/frontend/public/types/19.png new file mode 100644 index 0000000..e901bca Binary files /dev/null and b/frontend/public/types/19.png differ diff --git a/frontend/public/types/2.png b/frontend/public/types/2.png new file mode 100644 index 0000000..09caf9d Binary files /dev/null and b/frontend/public/types/2.png differ diff --git a/frontend/public/types/3.png b/frontend/public/types/3.png new file mode 100644 index 0000000..e0bbaeb Binary files /dev/null and b/frontend/public/types/3.png differ diff --git a/frontend/public/types/4.png b/frontend/public/types/4.png new file mode 100644 index 0000000..c54865c Binary files /dev/null and b/frontend/public/types/4.png differ diff --git a/frontend/public/types/5.png b/frontend/public/types/5.png new file mode 100644 index 0000000..ebb3e42 Binary files /dev/null and b/frontend/public/types/5.png differ diff --git a/frontend/public/types/6.png b/frontend/public/types/6.png new file mode 100644 index 0000000..1290970 Binary files /dev/null and b/frontend/public/types/6.png differ diff --git a/frontend/public/types/7.png b/frontend/public/types/7.png new file mode 100644 index 0000000..dae43fd Binary files /dev/null and b/frontend/public/types/7.png differ diff --git a/frontend/public/types/8.png b/frontend/public/types/8.png new file mode 100644 index 0000000..d46358e Binary files /dev/null and b/frontend/public/types/8.png differ diff --git a/frontend/public/types/9.png b/frontend/public/types/9.png new file mode 100644 index 0000000..7ad921d Binary files /dev/null and b/frontend/public/types/9.png differ diff --git a/frontend/public/types/small/1.png b/frontend/public/types/small/1.png new file mode 100644 index 0000000..eb814ca Binary files /dev/null and b/frontend/public/types/small/1.png differ diff --git a/frontend/public/types/small/10.png b/frontend/public/types/small/10.png new file mode 100644 index 0000000..e35f939 Binary files /dev/null and b/frontend/public/types/small/10.png differ diff --git a/frontend/public/types/small/11.png b/frontend/public/types/small/11.png new file mode 100644 index 0000000..60a891b Binary files /dev/null and b/frontend/public/types/small/11.png differ diff --git a/frontend/public/types/small/12.png b/frontend/public/types/small/12.png new file mode 100644 index 0000000..398f920 Binary files /dev/null and b/frontend/public/types/small/12.png differ diff --git a/frontend/public/types/small/13.png b/frontend/public/types/small/13.png new file mode 100644 index 0000000..123567f Binary files /dev/null and b/frontend/public/types/small/13.png differ diff --git a/frontend/public/types/small/14.png b/frontend/public/types/small/14.png new file mode 100644 index 0000000..a747e45 Binary files /dev/null and b/frontend/public/types/small/14.png differ diff --git a/frontend/public/types/small/15.png b/frontend/public/types/small/15.png new file mode 100644 index 0000000..f1a0c59 Binary files /dev/null and b/frontend/public/types/small/15.png differ diff --git a/frontend/public/types/small/16.png b/frontend/public/types/small/16.png new file mode 100644 index 0000000..e8bb756 Binary files /dev/null and b/frontend/public/types/small/16.png differ diff --git a/frontend/public/types/small/17.png b/frontend/public/types/small/17.png new file mode 100644 index 0000000..abe9ad8 Binary files /dev/null and b/frontend/public/types/small/17.png differ diff --git a/frontend/public/types/small/18.png b/frontend/public/types/small/18.png new file mode 100644 index 0000000..4803221 Binary files /dev/null and b/frontend/public/types/small/18.png differ diff --git a/frontend/public/types/small/2.png b/frontend/public/types/small/2.png new file mode 100644 index 0000000..4625a16 Binary files /dev/null and b/frontend/public/types/small/2.png differ diff --git a/frontend/public/types/small/3.png b/frontend/public/types/small/3.png new file mode 100644 index 0000000..100cdbc Binary files /dev/null and b/frontend/public/types/small/3.png differ diff --git a/frontend/public/types/small/4.png b/frontend/public/types/small/4.png new file mode 100644 index 0000000..6a67490 Binary files /dev/null and b/frontend/public/types/small/4.png differ diff --git a/frontend/public/types/small/5.png b/frontend/public/types/small/5.png new file mode 100644 index 0000000..2c7328e Binary files /dev/null and b/frontend/public/types/small/5.png differ diff --git a/frontend/public/types/small/6.png b/frontend/public/types/small/6.png new file mode 100644 index 0000000..1461096 Binary files /dev/null and b/frontend/public/types/small/6.png differ diff --git a/frontend/public/types/small/7.png b/frontend/public/types/small/7.png new file mode 100644 index 0000000..0cb98aa Binary files /dev/null and b/frontend/public/types/small/7.png differ diff --git a/frontend/public/types/small/8.png b/frontend/public/types/small/8.png new file mode 100644 index 0000000..1fb59c7 Binary files /dev/null and b/frontend/public/types/small/8.png differ diff --git a/frontend/public/types/small/9.png b/frontend/public/types/small/9.png new file mode 100644 index 0000000..c6b3e9d Binary files /dev/null and b/frontend/public/types/small/9.png differ diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 0415d99..8a0e36e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,6 +1,7 @@ diff --git a/frontend/src/components/AppShell.vue b/frontend/src/components/AppShell.vue new file mode 100644 index 0000000..1fd82d1 --- /dev/null +++ b/frontend/src/components/AppShell.vue @@ -0,0 +1,50 @@ + + + diff --git a/frontend/src/components/DetailSection.vue b/frontend/src/components/DetailSection.vue new file mode 100644 index 0000000..cfe9dd8 --- /dev/null +++ b/frontend/src/components/DetailSection.vue @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/components/EntityCard.vue b/frontend/src/components/EntityCard.vue new file mode 100644 index 0000000..6c76a49 --- /dev/null +++ b/frontend/src/components/EntityCard.vue @@ -0,0 +1,36 @@ + + + diff --git a/frontend/src/components/FilterPanel.vue b/frontend/src/components/FilterPanel.vue new file mode 100644 index 0000000..ea5d58d --- /dev/null +++ b/frontend/src/components/FilterPanel.vue @@ -0,0 +1,5 @@ + diff --git a/frontend/src/components/PageHeader.vue b/frontend/src/components/PageHeader.vue new file mode 100644 index 0000000..20e800f --- /dev/null +++ b/frontend/src/components/PageHeader.vue @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/components/PokeBallMark.vue b/frontend/src/components/PokeBallMark.vue new file mode 100644 index 0000000..f94a169 --- /dev/null +++ b/frontend/src/components/PokeBallMark.vue @@ -0,0 +1,14 @@ + + + diff --git a/frontend/src/components/StatusMessage.vue b/frontend/src/components/StatusMessage.vue new file mode 100644 index 0000000..27affd9 --- /dev/null +++ b/frontend/src/components/StatusMessage.vue @@ -0,0 +1,43 @@ + + + diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index d9732f0..e861d69 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -1,8 +1,42 @@ :root { - color: #17211b; - background: #f6f4ee; - font-family: - Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + color-scheme: light; + --pokemon-yellow: #ffcb05; + --pokemon-yellow-soft: #ffe46b; + --pokemon-blue: #2a75bb; + --pokemon-blue-deep: #003a70; + --pokemon-red: #ee1515; + --pokemon-red-deep: #cc0000; + --pokeball-black: #202124; + --pokeball-white: #f7f8fb; + --bg: #f2f5fa; + --bg-alt: #eaf1fb; + --surface: #ffffff; + --surface-raised: #ffffff; + --surface-soft: #f8fafd; + --ink: #151923; + --ink-soft: #354052; + --muted: #687487; + --line: #d8deea; + --line-strong: #1f2a3b; + --focus: #0b63ce; + --success: #2eb872; + --warning: #ffb800; + --danger: #df2f2f; + --radius-card: 8px; + --radius-control: 8px; + --radius-small: 6px; + --shadow-control: 0 3px 0 var(--line-strong); + --shadow-soft: 0 8px 22px rgba(23, 35, 54, 0.09); + --shadow-raised: 0 14px 32px rgba(23, 35, 54, 0.13); + --container: 1240px; + --font-sans: + Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", + sans-serif; + --font-display: "Arial Rounded MT Bold", "Nunito", "Avenir Next Rounded", var(--font-sans); + + color: var(--ink); + background: var(--bg); + font-family: var(--font-sans); font-synthesis: none; text-rendering: optimizeLegibility; } @@ -11,9 +45,25 @@ box-sizing: border-box; } +html { + scroll-behavior: smooth; + scrollbar-gutter: stable; +} + +@supports not (scrollbar-gutter: stable) { + html { + overflow-y: scroll; + } +} + body { min-width: 320px; margin: 0; + color: var(--ink); + background: + linear-gradient(90deg, rgba(42, 117, 187, 0.08) 1px, transparent 1px) 0 0 / 32px 32px, + linear-gradient(rgba(42, 117, 187, 0.08) 1px, transparent 1px) 0 0 / 32px 32px, + linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%); } a { @@ -23,97 +73,139 @@ a { button, input, -select { +select, +textarea { font: inherit; } +button { + border: 0; +} + +img, +svg { + display: block; + max-width: 100%; +} + +:focus-visible { + outline: 3px solid var(--focus); + outline-offset: 3px; +} + .app-shell { min-height: 100vh; } -.topbar { +.container { + width: min(100%, var(--container)); + margin: 0 auto; + padding: 0 24px; +} + +.site-header { position: sticky; top: 0; - z-index: 10; - display: flex; - align-items: center; - justify-content: space-between; - gap: 24px; - padding: 16px clamp(16px, 4vw, 48px); - border-bottom: 1px solid #d7d2c4; - background: rgba(246, 244, 238, 0.94); - backdrop-filter: blur(12px); + z-index: 50; + border-bottom: 1px solid rgba(31, 42, 59, 0.12); + background: color-mix(in srgb, var(--surface) 88%, transparent); + backdrop-filter: blur(18px); } -.topbar-main { - display: flex; +.top-nav { + min-height: 70px; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; - gap: 24px; - min-width: 0; + gap: 22px; } -.brand { - flex: 0 0 auto; - font-size: 20px; +.brand-lockup { + min-width: 216px; + display: inline-flex; + align-items: center; + gap: 12px; +} + +.pokemon-word { + display: inline-block; + color: var(--pokemon-yellow); + font-family: var(--font-display); + font-size: 28px; + font-weight: 900; + line-height: 0.9; + -webkit-text-stroke: 2px var(--pokemon-blue-deep); + text-shadow: 2px 3px 0 var(--pokemon-blue); +} + +.brand-subtitle { + display: block; + margin-top: 2px; + color: var(--muted); + font-size: 12px; font-weight: 800; - color: #1d3b2b; + text-transform: uppercase; } -.nav-tabs { +.nav-links { display: flex; - gap: 8px; + justify-content: center; + gap: 4px; overflow-x: auto; } -.nav-tabs a { - min-width: max-content; - padding: 8px 12px; - border-radius: 8px; - color: #4e5c52; - font-weight: 700; +.nav-links a { + min-height: 38px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8px 10px; + border-radius: var(--radius-control); + color: var(--ink-soft); + font-size: 14px; + font-weight: 850; + white-space: nowrap; } -.nav-tabs a.router-link-active { - background: #1f6f50; +.nav-links a:hover { + background: rgba(255, 203, 5, 0.24); + color: var(--ink); +} + +.nav-links a.router-link-active { + background: var(--pokemon-blue); color: #ffffff; } .auth-actions { display: flex; align-items: center; + justify-content: flex-end; gap: 8px; - flex: 0 0 auto; -} - -.auth-actions a, -.auth-actions .plain-button { - min-height: 36px; - padding: 7px 12px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #4e5c52; - font-weight: 800; -} - -.auth-actions a.router-link-active { - border-color: #1f6f50; - color: #1f5c40; } .auth-user { max-width: 180px; overflow: hidden; - color: #566156; - font-weight: 800; + color: var(--ink-soft); + font-size: 14px; + font-weight: 850; text-overflow: ellipsis; white-space: nowrap; } .page { - width: min(1180px, calc(100% - 32px)); + position: relative; + --page-padding-x: 24px; + width: min(100%, var(--container)); margin: 0 auto; - padding: 28px 0 56px; + padding: 30px var(--page-padding-x) 58px; +} + +.page-stack { + position: relative; + display: grid; + gap: 18px; } .page-header { @@ -121,51 +213,233 @@ select { align-items: end; justify-content: space-between; gap: 16px; - margin-bottom: 20px; +} + +.page-header__copy { + display: grid; + gap: 8px; + min-width: 0; +} + +.page-header__actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 8px; +} + +.page-kicker { + display: inline-flex; + align-items: center; + gap: 8px; + width: fit-content; + color: var(--pokemon-blue); + font-size: 13px; + font-weight: 900; + text-transform: uppercase; +} + +.page-kicker::before { + content: ""; + width: 18px; + height: 18px; + border: 3px solid var(--line-strong); + border-radius: 50%; + background: + linear-gradient(to bottom, var(--pokemon-red) 0 44%, var(--line-strong) 44% 56%, var(--surface) 56% 100%); } .page-title { margin: 0; - font-size: clamp(28px, 4vw, 40px); - line-height: 1.1; + color: var(--ink); + font-family: var(--font-display); + font-size: 42px; + font-weight: 950; + line-height: 1.08; } .page-subtitle { - margin: 8px 0 0; - color: #657067; + margin: 0; + color: var(--ink-soft); } +.pokeball-mark { + position: relative; + width: var(--ball-size, 34px); + height: var(--ball-size, 34px); + display: inline-block; + flex: 0 0 auto; + border: calc(var(--ball-size, 34px) * 0.07) solid var(--pokeball-black); + border-radius: 50%; + background: + linear-gradient( + to bottom, + var(--pokemon-red) 0 45%, + var(--pokeball-black) 45% 55%, + var(--pokeball-white) 55% 100% + ); + box-shadow: inset 0 4px 0 rgba(255, 255, 255, 0.45), 0 3px 0 rgba(0, 0, 0, 0.18); +} + +.pokeball-mark::after { + content: ""; + position: absolute; + inset: 50% auto auto 50%; + width: calc(var(--ball-size, 34px) * 0.34); + height: calc(var(--ball-size, 34px) * 0.34); + transform: translate(-50%, -50%); + border: calc(var(--ball-size, 34px) * 0.055) solid var(--pokeball-black); + border-radius: 50%; + background: var(--pokeball-white); + box-shadow: inset 0 0 0 calc(var(--ball-size, 34px) * 0.055) #dfe5ef; +} + +.ui-button, +.primary-button, +.link-button, +.plain-button, +.row-actions button, +.inline-row button, +.appearance-row button { + --btn-bg: var(--surface); + --btn-fg: var(--ink); + --btn-border: var(--line-strong); + min-height: 42px; + width: fit-content; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 9px 13px; + border: 2px solid var(--btn-border); + border-radius: var(--radius-control); + background: var(--btn-bg); + color: var(--btn-fg); + box-shadow: var(--shadow-control); + font-weight: 900; + line-height: 1.1; + cursor: pointer; + transition: + transform 0.14s ease, + box-shadow 0.14s ease, + background 0.14s ease, + border-color 0.14s ease; + white-space: nowrap; +} + +.ui-button:hover, +.primary-button:hover, +.link-button:hover, +.plain-button:hover, +.row-actions button:hover, +.inline-row button:hover, +.appearance-row button:hover { + transform: translateY(-2px); + box-shadow: 0 5px 0 var(--line-strong); +} + +.ui-button:active, +.primary-button:active, +.link-button:active, +.plain-button:active, +.row-actions button:active, +.inline-row button:active, +.appearance-row button:active { + transform: translateY(2px); + box-shadow: 0 1px 0 var(--line-strong); +} + +.ui-button--primary, +.primary-button { + --btn-bg: var(--pokemon-yellow); + --btn-fg: #172036; +} + +.ui-button--blue, +.link-button { + --btn-bg: var(--pokemon-blue); + --btn-fg: #ffffff; +} + +.ui-button--red { + --btn-bg: var(--pokemon-red); + --btn-fg: #ffffff; +} + +.ui-button--ghost, +.plain-button, +.row-actions button, +.inline-row button, +.appearance-row button { + --btn-bg: var(--surface); + --btn-border: var(--line); + box-shadow: none; +} + +.ui-button--small { + min-height: 36px; + padding: 7px 10px; + font-size: 14px; + box-shadow: 0 2px 0 var(--line-strong); +} + +button:disabled, +.ui-button:disabled, +.primary-button:disabled, +.link-button:disabled, +.plain-button:disabled { + cursor: not-allowed; + opacity: 0.54; + transform: none; + box-shadow: 0 2px 0 var(--line); +} + +.filter-panel, .toolbar { display: grid; - grid-template-columns: repeat(4, minmax(150px, 1fr)); - gap: 12px; - margin-bottom: 20px; + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: 14px; padding: 16px; - border: 1px solid #d7d2c4; - border-radius: 8px; - background: #ffffff; + border: 2px solid var(--line-strong); + border-radius: var(--radius-card); + background: var(--surface); + box-shadow: var(--shadow-control); } .field { display: grid; - gap: 6px; + gap: 7px; + align-content: start; } -.field label { - font-size: 12px; - font-weight: 800; - color: #566156; +.field label, +.field-label { + color: var(--ink-soft); + font-size: 14px; + font-weight: 850; } .field input, -.field select { +.field select, +.tags-select__search { width: 100%; - min-height: 40px; - padding: 8px 10px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #17211b; + min-height: 44px; + padding: 10px 12px; + border: 2px solid var(--line); + border-radius: var(--radius-control); + background: var(--surface); + color: var(--ink); + transition: + border-color 0.14s ease, + box-shadow 0.14s ease; +} + +.field input:focus, +.field select:focus, +.tags-select__search:focus { + border-color: var(--pokemon-blue); + box-shadow: 0 0 0 4px rgba(42, 117, 187, 0.16); + outline: none; } .tags-select { @@ -178,19 +452,19 @@ select { justify-content: space-between; gap: 8px; width: 100%; - min-height: 40px; - padding: 6px 10px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #17211b; + min-height: 44px; + padding: 7px 10px; + border: 2px solid var(--line); + border-radius: var(--radius-control); + background: var(--surface); + color: var(--ink); text-align: left; cursor: pointer; } .tags-select__trigger.open { - border-color: #1f6f50; - box-shadow: 0 0 0 3px rgba(31, 111, 80, 0.12); + border-color: var(--pokemon-blue); + box-shadow: 0 0 0 4px rgba(42, 117, 187, 0.16); } .tags-select__selected { @@ -205,72 +479,61 @@ select { align-items: center; justify-content: center; gap: 6px; - min-height: 26px; - padding: 3px 7px; - border-color: #9fc9a5; - border: 1px solid #9fc9a5; + min-height: 28px; + padding: 4px 8px; + border: 1px solid rgba(42, 117, 187, 0.28); border-radius: 999px; - background: #edf7ef; - color: #1f5c40; + background: rgba(42, 117, 187, 0.1); + color: var(--pokemon-blue-deep); font-size: 13px; - font-weight: 800; + font-weight: 850; } .tags-select__remove { + min-width: 18px; + min-height: 18px; display: inline-flex; align-items: center; justify-content: center; - min-width: 18px; - min-height: 18px; border-radius: 999px; - color: #4e5c52; + color: var(--ink-soft); cursor: pointer; } .tags-select__remove:hover { - background: rgba(31, 111, 80, 0.12); + background: rgba(42, 117, 187, 0.14); } .tags-select__placeholder { - color: #8a8275; + color: var(--muted); } .tags-select__arrow { flex: 0 0 auto; - color: #657067; - font-size: 16px; + color: var(--muted); + font-size: 18px; line-height: 1; } .tags-select__dropdown { position: absolute; - top: calc(100% + 4px); + top: calc(100% + 6px); left: 0; z-index: 40; display: grid; gap: 8px; width: 100%; - min-width: 220px; + min-width: 240px; padding: 8px; - border: 1px solid #d7d2c4; - border-radius: 8px; - background: #ffffff; - box-shadow: 0 12px 28px rgba(23, 33, 27, 0.16); -} - -.tags-select__search { - width: 100%; - min-height: 40px; - padding: 8px 10px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #17211b; + border: 2px solid var(--line-strong); + border-radius: var(--radius-card); + background: var(--surface); + box-shadow: var(--shadow-raised); } .tags-select__options { display: grid; - max-height: 220px; + max-height: 240px; overflow: auto; } @@ -283,9 +546,9 @@ select { min-height: 40px; padding: 8px 10px; border: 0; - border-radius: 6px; + border-radius: var(--radius-small); background: transparent; - color: #17211b; + color: var(--ink); text-align: left; cursor: pointer; } @@ -293,27 +556,23 @@ select { .tags-select__option:hover, .tags-select__option.active, .tags-select__option.selected { - background: #edf7ef; - color: #1f5c40; + background: rgba(255, 203, 5, 0.22); + color: var(--pokemon-blue-deep); } .tags-select__option.active { - box-shadow: inset 0 0 0 2px rgba(31, 111, 80, 0.18); + box-shadow: inset 0 0 0 2px rgba(42, 117, 187, 0.2); } .tags-select__option.selected { - font-weight: 800; + font-weight: 850; } .tags-select__create { - border-top: 1px solid #ebe6da; + border-top: 1px solid var(--line); border-radius: 0; - color: #1f6f50; - font-weight: 800; -} - -.tags-select__create:hover { - background: #edf7ef; + color: var(--pokemon-blue); + font-weight: 850; } .tags-select__option:disabled { @@ -323,80 +582,143 @@ select { .tags-select__state { flex: 0 0 auto; - color: #1f6f50; + color: var(--pokemon-blue); font-size: 12px; - font-weight: 800; + font-weight: 850; } .tags-select__empty { margin: 0; padding: 8px 10px; - color: #657067; + color: var(--muted); font-size: 13px; } .segmented { display: inline-flex; + flex-wrap: wrap; width: fit-content; - padding: 3px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #f1eee5; + gap: 4px; + padding: 4px; + border: 2px solid var(--line); + border-radius: var(--radius-control); + background: var(--surface-soft); } .segmented button { min-width: 52px; - min-height: 32px; - border: 0; - border-radius: 6px; + min-height: 34px; + padding: 6px 10px; + border-radius: var(--radius-small); background: transparent; - color: #566156; + color: var(--ink-soft); + font-weight: 850; cursor: pointer; } .segmented button.active { - background: #ffffff; - color: #1f6f50; - font-weight: 800; - box-shadow: 0 1px 4px rgba(31, 111, 80, 0.16); + background: var(--pokemon-blue); + color: #ffffff; } +.tabs { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.tabs button { + min-height: 42px; + padding: 9px 13px; + border: 2px solid var(--line); + border-radius: var(--radius-control); + background: var(--surface); + color: var(--ink-soft); + font-weight: 900; + cursor: pointer; +} + +.tabs button.active { + border-color: var(--line-strong); + background: var(--pokemon-yellow); + color: #172036; + box-shadow: 0 2px 0 var(--line-strong); +} + +.entity-grid, .grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); - gap: 14px; -} - -.entity-card, -.detail-section { - border: 1px solid #d7d2c4; - border-radius: 8px; - background: #ffffff; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; } .entity-card { + min-height: 164px; display: grid; + grid-template-columns: auto minmax(0, 1fr); gap: 12px; padding: 16px; + border: 2px solid var(--line-strong); + border-radius: var(--radius-card); + background: var(--surface); + box-shadow: var(--shadow-control); + color: var(--ink); } -.entity-card h2, -.entity-card h3, -.detail-section h2 { - margin: 0; - font-size: 18px; +.entity-card--link { + transition: + transform 0.16s ease, + box-shadow 0.16s ease, + border-color 0.16s ease; } +.entity-card--link:hover { + transform: translateY(-2px); + border-color: var(--pokemon-blue); + box-shadow: 0 5px 0 var(--line-strong); +} + +.entity-card__mark { + width: 42px; + height: 42px; + display: inline-grid; + place-items: center; + border: 2px solid var(--line-strong); + border-radius: var(--radius-control); + background: var(--pokemon-yellow); + box-shadow: 0 3px 0 var(--line-strong); + color: #172036; + font-family: var(--font-display); + font-weight: 950; +} + +.entity-card__content { + display: grid; + align-content: start; + gap: 10px; + min-width: 0; +} + +.entity-card__title { + color: var(--ink); + font-family: var(--font-display); + font-size: 21px; + font-weight: 950; + line-height: 1.12; + overflow-wrap: anywhere; +} + +.entity-card__subtitle, .meta-line { margin: 0; - color: #657067; + color: var(--muted); } .edit-meta { margin: 0; - color: #7b766a; + color: var(--muted); font-size: 13px; - font-weight: 700; + font-weight: 750; } .chips { @@ -410,31 +732,60 @@ select { align-items: center; min-height: 28px; padding: 4px 8px; - border: 1px solid #cddfce; + border: 1px solid rgba(42, 117, 187, 0.28); border-radius: 999px; - background: #edf7ef; - color: #1f5c40; + background: rgba(42, 117, 187, 0.1); + color: var(--pokemon-blue-deep); font-size: 13px; - font-weight: 700; + font-weight: 800; } .detail-grid { display: grid; - grid-template-columns: 1fr 1fr; - gap: 14px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; } .detail-section { + display: grid; + gap: 12px; padding: 18px; + border: 1px solid var(--line); + border-radius: var(--radius-card); + background: var(--surface); + box-shadow: var(--shadow-soft); +} + +.detail-section__header, +.detail-section > h2 { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; } .detail-section h2 { - margin-bottom: 12px; + margin: 0; + color: var(--ink); + font-family: var(--font-display); + font-size: 21px; + font-weight: 950; + line-height: 1.12; +} + +.detail-section__body { + display: grid; + gap: 12px; +} + +.detail-section a:not(.ui-button) { + color: var(--pokemon-blue-deep); + font-weight: 850; } .row-list { display: grid; - gap: 8px; + gap: 0; margin: 0; padding: 0; list-style: none; @@ -445,8 +796,8 @@ select { align-items: center; justify-content: space-between; gap: 12px; - padding: 10px 0; - border-bottom: 1px solid #ebe6da; + padding: 11px 0; + border-bottom: 1px solid var(--line); } .row-list li:last-child { @@ -469,7 +820,7 @@ select { gap: 4px; width: 100%; margin: 0; - color: #657067; + color: var(--muted); text-align: left; } @@ -485,67 +836,84 @@ select { } .appearance-summary dt { - color: #566156; - font-weight: 800; + color: var(--ink-soft); + font-weight: 850; } -.link-button { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 36px; - width: fit-content; - padding: 7px 12px; - border-radius: 8px; - background: #a83f39; - color: #ffffff; +.status-message { + position: absolute; + right: 0; + left: 0; + z-index: 80; + display: flex; + align-items: start; + gap: 10px; + width: auto; + margin: 0; + padding: 14px; + border: 1px solid var(--status-line, var(--line)); + border-left: 6px solid var(--status-accent, var(--pokemon-blue)); + border-radius: var(--radius-card); + background: var(--status-bg, var(--surface)); + box-shadow: var(--shadow-raised); + color: var(--ink-soft); font-weight: 800; + pointer-events: none; + opacity: 1; + transform: translateY(0); + visibility: visible; + transition: + opacity 0.18s ease, + transform 0.18s ease, + visibility 0.18s ease; } -.primary-button { - display: inline-flex; - align-items: center; - justify-content: center; - width: fit-content; - min-height: 40px; - padding: 8px 14px; - border: 0; - border-radius: 8px; - background: #1f6f50; - color: #ffffff; - font-weight: 800; - cursor: pointer; +.page > .status-message { + right: var(--page-padding-x); + left: var(--page-padding-x); +} + +.status-message--hidden { + opacity: 0; + transform: translateY(-6px); + visibility: hidden; +} + +.status-message::before { + content: ""; + width: 12px; + height: 12px; + flex: 0 0 auto; + margin-top: 6px; + border-radius: 50%; + background: var(--status-accent, var(--pokemon-blue)); +} + +.status-message--success { + --status-accent: var(--success); + --status-line: color-mix(in srgb, var(--success) 38%, var(--line)); + --status-bg: color-mix(in srgb, var(--success) 10%, var(--surface)); +} + +.status-message--warning { + --status-accent: var(--warning); + --status-line: color-mix(in srgb, var(--warning) 42%, var(--line)); + --status-bg: color-mix(in srgb, var(--warning) 12%, var(--surface)); +} + +.status-message--danger { + --status-accent: var(--danger); + --status-line: color-mix(in srgb, var(--danger) 38%, var(--line)); + --status-bg: color-mix(in srgb, var(--danger) 10%, var(--surface)); } .status { - padding: 18px; - border: 1px solid #d7d2c4; - border-radius: 8px; - background: #ffffff; - color: #657067; -} - -.tabs { - display: flex; - gap: 8px; - margin-bottom: 16px; -} - -.tabs button { - min-height: 40px; - padding: 8px 14px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #566156; - cursor: pointer; -} - -.tabs button.active { - border-color: #1f6f50; - background: #1f6f50; - color: #ffffff; - font-weight: 800; + margin: 0; + padding: 14px; + border: 1px solid var(--line); + border-radius: var(--radius-card); + background: var(--surface); + color: var(--muted); } .auth-page { @@ -555,54 +923,60 @@ select { } .auth-panel { + position: relative; + width: min(480px, 100%); display: grid; gap: 18px; - width: min(460px, 100%); padding: 22px; - border: 1px solid #d7d2c4; - border-radius: 8px; - background: #ffffff; + border: 2px solid var(--line-strong); + border-radius: var(--radius-card); + background: var(--surface); + box-shadow: var(--shadow-control); } .auth-panel .page-header { - margin-bottom: 0; + display: block; +} + +.auth-panel .page-title { + font-size: 34px; } .auth-form { + position: relative; display: grid; gap: 14px; } +.auth-switch { + margin: 0; + color: var(--muted); +} + +.auth-switch a { + color: var(--pokemon-blue-deep); + font-weight: 900; +} + .auth-message { margin: 0; padding: 10px 12px; - border: 1px solid #b9d8bd; - border-radius: 8px; - background: #edf7ef; - color: #1f5c40; + border: 1px solid color-mix(in srgb, var(--success) 38%, var(--line)); + border-radius: var(--radius-card); + background: color-mix(in srgb, var(--success) 10%, var(--surface)); + color: var(--ink-soft); font-weight: 800; } .auth-message.error { - border-color: #e0b0ac; - background: #fff0ee; - color: #a83f39; -} - -.auth-switch { - margin: 0; - color: #657067; -} - -.auth-switch a { - color: #1f6f50; - font-weight: 800; + border-color: color-mix(in srgb, var(--danger) 38%, var(--line)); + background: color-mix(in srgb, var(--danger) 10%, var(--surface)); } .admin-layout { display: grid; - grid-template-columns: minmax(320px, 420px) 1fr; - gap: 14px; + grid-template-columns: minmax(320px, 420px) minmax(0, 1fr); + gap: 16px; align-items: start; } @@ -623,34 +997,28 @@ select { .row-actions { flex: 0 0 auto; + flex-wrap: wrap; + justify-content: flex-end; } .row-actions button, -.plain-button, .inline-row button, .appearance-row button { min-height: 34px; padding: 6px 10px; - border: 1px solid #c7c0b2; - border-radius: 8px; - background: #fffdfa; - color: #4e5c52; - cursor: pointer; -} - -.plain-button { - width: fit-content; + font-size: 14px; } .inline-row { align-items: center; } -.inline-row select { +.inline-row .tags-select { flex: 1; + min-width: 180px; } -.inline-row .tags-select { +.inline-row select { flex: 1; } @@ -658,40 +1026,56 @@ select { width: 90px; } +.check-row label { + display: inline-flex; + align-items: center; + gap: 7px; + min-height: 36px; + color: var(--ink-soft); + font-weight: 850; + cursor: pointer; +} + +.check-row input { + width: 18px; + height: 18px; + accent-color: var(--pokemon-blue); +} + .appearance-row { display: grid; grid-template-columns: 1fr; padding: 12px; - border: 1px solid #ebe6da; - border-radius: 8px; - background: #faf8f1; + border: 1px solid var(--line); + border-radius: var(--radius-card); + background: var(--surface-soft); } .appearance-row input { min-width: 64px; } -button:disabled { - cursor: not-allowed; - opacity: 0.6; -} - -@media (max-width: 760px) { - .topbar { - align-items: start; - flex-direction: column; +@media (max-width: 900px) { + .top-nav { + grid-template-columns: 1fr; + gap: 12px; + padding-top: 14px; + padding-bottom: 14px; } - .topbar-main { - align-items: start; - flex-direction: column; - gap: 12px; + .brand-lockup, + .auth-actions, + .nav-links { + justify-content: flex-start; + min-width: 0; + } + + .nav-links { width: 100%; } .auth-actions { flex-wrap: wrap; - width: 100%; } .page-header { @@ -699,22 +1083,63 @@ button:disabled { flex-direction: column; } - .toolbar, + .page-header__actions { + justify-content: flex-start; + } + .detail-grid, .admin-layout { grid-template-columns: 1fr; } +} - .appearance-row { +@media (max-width: 640px) { + .container, + .page { + --page-padding-x: 16px; + padding-right: var(--page-padding-x); + padding-left: var(--page-padding-x); + } + + .page { + padding-top: 22px; + padding-bottom: 44px; + } + + .page-title { + font-size: 32px; + } + + .pokemon-word { + font-size: 25px; + } + + .filter-panel, + .toolbar, + .entity-grid, + .grid { + grid-template-columns: 1fr; + } + + .entity-card { grid-template-columns: 1fr; } .appearance-list li { - align-items: start; grid-template-columns: 1fr; } - .appearance-list { - overflow-x: auto; + .appearance-summary div { + grid-template-columns: 68px minmax(0, 1fr); + } + + .inline-row { + align-items: stretch; + flex-direction: column; + } + + .inline-row input, + .inline-row .tags-select { + width: 100%; } } diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue index d23b2a8..6f478ac 100644 --- a/frontend/src/views/AdminView.vue +++ b/frontend/src/views/AdminView.vue @@ -1,5 +1,7 @@