feat: 侧边卡片设置页视觉重构——分组容器卡片、计数徽标、自定义开关、加宽设置弹窗(描述 + 完成按钮),卡片齿轮与勾选徽标纵向对齐同尺寸

This commit is contained in:
Menghuan1918
2026-08-14 14:07:52 +08:00
parent efb2e2bd58
commit 047f48337d
4 changed files with 408 additions and 181 deletions
+209 -70
View File
@@ -1,20 +1,28 @@
/**
* Side card settings section chrome (DSH-native tokens throughout):
* - the section is a column of GROUP CONTAINER cards (the PluginCard recipe:
* l2 hairline, 16px radius, layer-3 fill) with a heading + inventory count
* badge (the settings catalogHeading recipe), opening with a one-line
* intro (the DSH section heading+intro recipe);
* - 常规 holds full-width settings rows (the DSH settings-row recipe:
* title/desc left + control right, hairline separators) whose toggles are
* custom switches — a real checkbox (native semantics and focus) driving
* a styled track/thumb;
* - the feature inventories are responsive GRIDS of small toggle cards
* (auto-fill columns — several cards per row, wrapping);
* - a card's on/off state IS its visual state: enabled = brand border +
* tinted fill + a check badge pinned to the far right of the top row,
* disabled = neutral hairline + dimmed text;
* (auto-fill columns — several cards per row, wrapping): icon chip +
* title + type id, the card's on/off state IS its visual state
* (enabled = brand border + tinted fill + circular check badge,
* disabled = neutral hairline + dimmed);
* - features that declare related settings carry a gear corner button
* opening a native Modal whose rows are the DSH settings-row recipe
* (title/desc left + native accent checkbox, hairline separators);
* opening a native Modal (wider than the primitive default) whose rows
* follow the same row recipe with switch controls and a Done footer;
* - the width input stays a native settings row (a value, not a switch).
*/
.section {
display: flex;
flex-direction: column;
gap: 8px;
gap: 14px;
width: 100%;
/* Tall inventories (7 tab cards + 9 viewer cards + general rows) must
never be clipped by the settings shell: fill the shell's options area
@@ -24,15 +32,55 @@
overflow-y: auto;
}
.section > :last-child {
border-bottom: none;
/* One-line section intro (the DSH section heading+intro recipe). */
.intro {
margin: 0;
padding: 0 2px;
font-size: 13px;
line-height: 20px;
color: var(--dsw-alias-label-tertiary);
}
/* Group container: the DSH card recipe (PluginCard) — l2 hairline, r16,
layer-3 fill; holds its own heading + stacked rows / card grid. */
.group {
flex: none;
display: flex;
flex-direction: column;
gap: 8px;
padding: 18px 20px 20px;
box-sizing: border-box;
border: 1px solid var(--dsw-alias-border-l2);
border-radius: 16px;
background: var(--dsw-alias-bg-layer-3);
}
/* Group heading: 13/600 + the inventory count badge (the settings
catalogHeading recipe: count in tabular-nums, tertiary). */
.groupHeading {
display: flex;
align-items: baseline;
gap: 7px;
padding: 0 2px 6px;
font-size: 13px;
line-height: 20px;
font-weight: 600;
color: var(--dsw-alias-label-primary);
}
.count {
font-size: 12px;
line-height: 18px;
font-weight: 400;
color: var(--dsw-alias-label-tertiary);
font-variant-numeric: tabular-nums;
}
/* Responsive inventory: as many small cards per row as the width allows. */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
gap: 10px;
}
/* One small toggle card (visual chrome; the interactive part is .cardMain). */
@@ -40,7 +88,7 @@
position: relative;
display: flex;
flex-direction: column;
border-radius: 8px;
border-radius: 12px;
border: 1px solid var(--dsw-alias-border-l2);
background: transparent;
font: inherit;
@@ -52,6 +100,7 @@
/* Hover affordance on disabled cards only (enabled cards are already lit). */
.card:not(.cardOn):hover {
background: var(--dsw-alias-interactive-bg-hover);
border-color: var(--dsw-alias-label-dimmed);
}
/* Enabled = highlighted: brand border + tinted fill. */
@@ -64,9 +113,9 @@
.cardMain {
display: flex;
flex-direction: column;
gap: 2px;
gap: 6px;
width: 100%;
padding: 10px 12px;
padding: 12px;
border: 0;
border-radius: inherit;
background: transparent;
@@ -85,18 +134,28 @@
.cardTop {
display: flex;
align-items: center;
gap: 6px;
gap: 8px;
min-width: 0;
}
.cardIcon {
/* The icon sits in a rounded chip: brand-tinted while the card is on,
neutral (layer-2 fill + hairline) while off. */
.cardIconChip {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
width: 28px;
height: 28px;
border-radius: 8px;
border: 1px solid var(--dsw-alias-border-l2);
background: var(--dsw-alias-bg-layer-2);
color: var(--dsw-alias-label-tertiary);
}
.cardOn .cardIcon {
.cardOn .cardIconChip {
border-color: color-mix(in srgb, var(--dsw-alias-button-primary-fill) 35%, transparent);
background: color-mix(in srgb, var(--dsw-alias-button-primary-fill) 12%, transparent);
color: var(--dsw-alias-button-primary-fill);
}
@@ -105,6 +164,7 @@
min-width: 0;
font-size: 13px;
line-height: 20px;
font-weight: 600;
color: var(--dsw-alias-label-secondary);
white-space: nowrap;
overflow: hidden;
@@ -115,12 +175,18 @@
color: var(--dsw-alias-label-primary);
}
/* The check badge: pinned to the FAR RIGHT of the top row (enabled only). */
/* The check badge: a circular brand-filled pin at the card's far right
(enabled only), the icon in the inverted surface token (the .save recipe). */
.cardCheck {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
color: var(--dsw-alias-button-primary-fill);
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--dsw-alias-button-primary-fill);
color: var(--dsw-alias-bg-layer-3);
}
.cardDesc {
@@ -136,24 +202,28 @@
color: var(--dsw-alias-label-secondary);
}
/* The gear corner button: bottom-right of cards that declare related
settings; the desc gets right padding so the ellipsis never runs under it. */
/* Cards with a settings gear keep the desc clear of it: the gear sits in
the desc row, sharing the check badge's right edge (a vertical column of
controls at the card's right side). */
.cardWithGear .cardDesc {
padding-right: 18px;
padding-right: 30px;
}
/* The gear corner button: directly UNDER the check badge on the card's
right edge — the two controls are vertically aligned (same right inset,
one row apart) and the SAME 16px size, not side by side. */
.cardGear {
position: absolute;
right: 6px;
bottom: 6px;
right: 12px;
top: 46px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
width: 16px;
height: 16px;
padding: 0;
border: 0;
border-radius: 6px;
border-radius: 50%;
background: transparent;
color: var(--dsw-alias-label-tertiary);
cursor: pointer;
@@ -164,26 +234,21 @@
background: var(--dsw-alias-interactive-bg-hover);
}
/* Inventory section heading (常规 / 侧边栏内容 / 文件预览). */
.sectionHeading {
padding: 14px 0 0;
font-size: 12px;
line-height: 17px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--dsw-alias-label-secondary);
}
/* Native settings row recipe (the width input row + popup rows). */
/* Settings-row recipe (the DSH General section rows): title/desc left,
control right, hairline separators (stripped on the last row). */
.row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 0;
padding: 12px 2px;
border-bottom: 1px solid var(--dsw-alias-border-l2);
}
.row:last-child {
border-bottom: none;
}
.rowText {
display: flex;
flex-direction: column;
@@ -199,45 +264,66 @@
.desc {
font-size: 12px;
line-height: 17px;
color: var(--dsw-alias-label-secondary);
line-height: 18px;
color: var(--dsw-alias-label-tertiary);
}
/* Native checkbox: 16px, brand accent (the settings-row recipe). */
.toggle {
/* Custom switch: a real checkbox (native semantics + focus, visually
hidden) driving a styled 36x20 track with a sliding thumb. */
.switch {
position: relative;
display: inline-flex;
flex: none;
width: 16px;
height: 16px;
cursor: pointer;
}
.switchInput {
position: absolute;
width: 1px;
height: 1px;
margin: 0;
accent-color: var(--dsw-alias-button-primary-fill);
cursor: pointer;
opacity: 0;
}
.toggle:focus-visible {
outline: 2px solid var(--dsw-alias-border-l4);
outline-offset: 2px;
}
/* Secondary settings popup body: stacked native rows, trailing separator
stripped by the trailing-child rule. */
.popupRows {
display: flex;
flex-direction: column;
width: 100%;
}
.popupRow {
display: flex;
.switchTrack {
display: inline-flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 0;
border-bottom: 1px solid var(--dsw-alias-border-l2);
cursor: pointer;
width: 36px;
height: 20px;
padding: 2px;
box-sizing: border-box;
border-radius: 10px;
border: 1px solid var(--dsw-alias-border-l2);
background: var(--dsw-alias-bg-layer-1);
transition: background 0.15s ease, border-color 0.15s ease;
}
.popupRows > :last-child {
border-bottom: none;
.switchThumb {
display: block;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--dsw-alias-label-secondary);
transition: transform 0.15s ease, background 0.15s ease;
}
.switch:hover .switchTrack {
border-color: var(--dsw-alias-label-dimmed);
}
.switchInput:checked + .switchTrack {
border-color: var(--dsw-alias-button-primary-fill);
background: var(--dsw-alias-button-primary-fill);
}
.switchInput:checked + .switchTrack .switchThumb {
transform: translateX(16px);
background: var(--dsw-alias-bg-layer-3);
}
.switchInput:focus-visible + .switchTrack {
outline: 2px solid var(--dsw-alias-state-business-primary);
outline-offset: 2px;
}
.control {
@@ -257,6 +343,57 @@
color: var(--dsw-alias-label-secondary);
}
/* Secondary settings popup: a wider dialog than the primitive default (the
dialog portals to document.body; the doubled class keeps the width rule
above the primitive's .dialog regardless of stylesheet order). */
.popupDialog.popupDialog {
width: min(460px, 100%);
}
/* Popup rows: the same settings-row recipe with switch controls. */
.popupRows {
display: flex;
flex-direction: column;
width: 100%;
}
.popupRow {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px 2px;
border-bottom: 1px solid var(--dsw-alias-border-l2);
}
.popupRow:hover {
background: var(--dsw-alias-interactive-bg-hover);
}
.popupRows > :last-child {
border-bottom: none;
}
/* The popup's Done button: the settings primary-button recipe
(PluginCard .save: label-primary fill, inverted surface text). */
.done {
appearance: none;
border: 1px solid transparent;
border-radius: 8px;
padding: 5px 14px;
font: inherit;
font-size: 13px;
line-height: 1.5;
cursor: pointer;
background: var(--dsw-alias-label-primary);
color: var(--dsw-alias-bg-layer-3);
}
.done:focus-visible {
outline: 2px solid var(--dsw-alias-brand-primary);
outline-offset: 1px;
}
.error {
padding: 10px 0 2px;
font-size: 12px;
@@ -265,7 +402,9 @@
}
@media (prefers-reduced-motion: reduce) {
.card {
.card,
.switchTrack,
.switchThumb {
transition: none;
}
}