Files
deepseek-harness/packages/client/ui-question/src/client/QuestionComposer.module.css
T
2026-08-04 16:25:09 +08:00

408 lines
9.4 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* The takeover seats where the input card sits, centered on the InputBar
axis at the shared content width (input card - 32): sides = clearance +
16px so the relation also holds on narrow viewports. */
.frame {
display: flex;
justify-content: center;
padding: 6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;
}
/* Figma Input 973:36348 body over the 1019:36938 header: no banner strip —
the card keeps zero padding and sections carry their own insets. */
.card {
display: flex;
flex-direction: column;
width: 100%;
max-width: var(--dsh-chat-content-width);
/* Composer seat sits in a fixed-height conversation column (overflow
hidden): cap the card against the viewport and scroll the option list
so header and footer actions stay reachable on long batches. */
max-height: min(60vh, 520px);
padding: 0 0 10px;
border: 1px solid var(--dsw-alias-border-l2-darkmode-thin);
border-radius: 20px;
background: var(--dsw-specific-input-major);
box-shadow: var(--dsw-shadow-lv2);
color: var(--dsw-alias-label-primary);
overflow: hidden;
/* Elevated surface in dark, same as the menus: the body inside scrolls
once the card hits the cap above, so the thumb takes the l2 pair. Declared
on the card because the elevation belongs to the surface, and the custom
properties inherit down to `.body` (see ui-theme styles/scrollbar.css
for the rebinding contract). */
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
}
.card,
.card * {
box-sizing: border-box;
}
/* Figma 1019:36938 header, user-tuned: heading block left, close right; the
pager sits in the footer to balance the card. */
.header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
flex-shrink: 0;
padding: 20px 16px 0 24px;
}
.headingBlock {
min-width: 0;
}
.eyebrow {
/* Eyebrow-to-title gap widened from the figma 2px (user-tuned). */
margin-bottom: 5px;
color: var(--dsw-alias-label-tertiary);
font-size: 11px;
line-height: 16px;
}
.title {
margin: 0;
font-size: 16px;
line-height: 22px;
font-weight: 500;
}
.detail {
margin: 0 2px 8px;
}
.footerActions {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.pager {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.progress {
padding: 0 4px;
color: var(--dsw-alias-label-secondary);
font-size: 14px;
line-height: 24px;
font-weight: 500;
white-space: nowrap;
/* Narrow the plain spaces around the slash without touching glyph tracking. */
word-spacing: -2px;
}
.iconButton {
display: grid;
place-items: center;
width: 24px;
height: 24px;
padding: 0;
border: none;
border-radius: 999px;
background: transparent;
color: var(--dsw-alias-label-tertiary);
cursor: pointer;
}
.iconButton:hover:not(:disabled) {
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-primary);
}
.iconButton:disabled {
color: var(--dsw-alias-label-dimmed);
cursor: default;
}
.body {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
}
.options {
display: flex;
flex-direction: column;
gap: 1px;
margin: 8px 0 0;
padding: 4px 12px;
}
.option {
display: flex;
/* flex-start, not center: with a wrapped description the indicator must
stay on the FIRST line (centering drifts it down the taller copy block).
The 8px padding makes a single-line row 40px exactly, so nothing reads
as top-heavy; .number/.checkbox re-center against the first line box. */
align-items: flex-start;
gap: 8px;
width: 100%;
min-height: 40px;
/* Rows are the scroll content, never the slack absorber: a shrinkable row
collapses to min-height while its wrapped copy keeps the taller
intrinsic height, and centered content then paints outside the row box —
over the title and the next row. Overflow belongs to .options. */
flex-shrink: 0;
padding: 8px 12px 8px 8px;
border: 1px solid transparent;
border-radius: 12px;
background: transparent;
color: inherit;
text-align: left;
cursor: pointer;
transition: background-color 120ms ease, border-color 120ms ease;
}
.option:hover:not(:disabled),
.optionSelected {
background: var(--dsw-alias-interactive-bg-hover);
}
.optionSelected {
border-color: var(--dsw-alias-border-l2);
}
.option:disabled {
cursor: default;
}
/* Leading indicator (figma 20×20, radius 6): single-select shows the option
number, multi-select swaps in a checkbox; the custom-answer row follows —
its checkbox mirrors the typed draft (styling only, exclusivity holds). */
.number {
display: grid;
place-items: center;
flex: 0 0 20px;
width: 20px;
height: 20px;
/* (24px first-line box − 20px indicator) / 2: centers the indicator against
the first text line under the row's flex-start alignment. */
margin-top: 2px;
border-radius: 6px;
background: var(--dsw-alias-bg-overlay);
color: var(--dsw-alias-label-secondary);
font-size: 12px;
font-weight: 500;
line-height: 18px;
}
/* Multi-select box (figma 1055:41594, user-tuned down to 14×14): a radius-4
box centered in the 20px indicator seat; the box itself is the ::before
layer so the check icon stacks over it in the same grid cell. */
.checkbox {
display: grid;
place-items: center;
flex: 0 0 20px;
width: 20px;
height: 20px;
/* Same first-line centering as .number under flex-start alignment. */
margin-top: 2px;
}
.checkbox::before {
content: '';
grid-area: 1 / 1;
width: 14px;
height: 14px;
border: 1px solid var(--dsw-alias-border-l4);
border-radius: 4px;
transition: background-color 120ms ease, border-color 120ms ease;
}
.checkbox > svg {
grid-area: 1 / 1;
}
/* Checked: label-primary fill with a primary-foreground check — the pair
inverts with the theme (dark fill in light mode, light fill in dark mode). */
.checkboxChecked {
color: var(--dsw-alias-label-primary-foreground);
}
.checkboxChecked::before {
border-color: var(--dsw-alias-label-primary);
background: var(--dsw-alias-label-primary);
}
.optionCopy {
min-width: 0;
flex: 1;
}
.optionLine {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 2px 6px;
}
.optionLabel {
font-size: 14px;
line-height: 24px;
font-weight: 500;
}
.badge {
padding: 0 4px;
border-radius: 6px;
background: var(--dsw-specific-sidebar-nav-item-active-accent);
color: var(--dsw-alias-button-info-fill);
font-size: 11px;
line-height: 18px;
font-weight: 600;
}
.description {
color: var(--dsw-alias-label-tertiary);
font-size: 14px;
line-height: 24px;
font-weight: 400;
}
/* Custom answer row (figma 973:36427): an option-shaped row whose copy is an
inline text input; focus or a typed draft lifts it to the selected look. */
.customRow {
display: flex;
/* Same first-line alignment as .option — the indicator seat carries the
2px re-centering margin. */
align-items: flex-start;
gap: 8px;
width: 100%;
min-height: 40px;
/* Same reason as .option: the custom row is scroll content, and shrinking
it pushes the inline input past the footer. */
flex-shrink: 0;
padding: 8px 12px 8px 8px;
border: 1px solid transparent;
border-radius: 12px;
transition: background-color 120ms ease, border-color 120ms ease;
}
.customRow:hover,
.customRow:focus-within,
.customRowActive {
background: var(--dsw-alias-interactive-bg-hover);
}
.customRow:focus-within,
.customRowActive {
border-color: var(--dsw-alias-border-l2);
}
.customInput {
flex: 1;
min-width: 0;
padding: 0;
border: none;
outline: none;
background: transparent;
color: var(--dsw-alias-label-primary);
caret-color: var(--dsw-alias-state-business-primary);
font: inherit;
font-size: 14px;
line-height: 24px;
}
.customInput::placeholder {
color: var(--dsw-alias-label-caption);
}
/* Optionless question: the free-form answer is the whole body. The 12px side
margins add to the .options 12px padding so both edges align with the
title's 24px inset; type matches the option rows, no resize handle. */
.customTextarea {
display: block;
min-height: 64px;
max-height: 140px;
flex-shrink: 0;
margin: 0 12px;
padding: 8px 12px;
resize: none;
border: 1px solid var(--dsw-alias-border-l2);
border-radius: 10px;
outline: none;
background: var(--dsw-alias-bg-module-platform);
color: var(--dsw-alias-label-primary);
caret-color: var(--dsw-alias-state-business-primary);
font: inherit;
font-size: 14px;
line-height: 24px;
}
.customTextarea:focus {
border-color: var(--dsw-alias-state-business-primary);
}
.customTextarea::placeholder {
color: var(--dsw-alias-label-caption);
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-shrink: 0;
margin-top: 12px;
padding: 0 10px 0 18px;
}
.feedback {
flex: 1;
min-height: 16px;
color: var(--dsw-alias-state-error-primary);
font-size: 11px;
line-height: 16px;
text-align: right;
}
@media (max-width: 720px) {
.card {
border-radius: 16px;
}
.header {
padding: 10px 12px 0 18px;
}
.options {
padding: 4px 8px;
}
.title {
font-size: 15px;
line-height: 21px;
}
.option,
.customRow {
padding: 8px 6px;
}
.footer {
align-items: flex-end;
padding: 0 10px;
}
.footerActions {
flex-shrink: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.option,
.customRow {
transition: none;
}
}