Unify disabled appearance across all form controls
Disabled controls looked inconsistent: the SearchSelect faded (opacity-50) while native inputs used a solid strong surface. Standardize on the faded look (opacity-50) the user preferred, via shared constants so every form element matches: - DISABLED_CONTROL_CLASS (disabled:opacity-50 disabled:cursor-not-allowed) on the control — native inputs/select/textarea via PrimitiveWidgetsMixin, plus the Checkbox component (previously had no disabled style). - DISABLED_WITHIN_CLASS (has-[:disabled]: wrapper variant) for composite controls like SearchSelect whose disabled state lives on an inner element. e2e asserts a disabled SearchSelect and the Name input fade identically (opacity 0.5) and return to 1 when enabled. CLAUDE.md documents the shared disabled constants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,14 @@ from typing import TypedDict
|
||||
|
||||
|
||||
from common.components.core import Attributes, Element, HTMLAttribute, Media, Node
|
||||
from common.components.primitives import Div, Input, Pill, Span, Template
|
||||
from common.components.primitives import (
|
||||
DISABLED_WITHIN_CLASS,
|
||||
Div,
|
||||
Input,
|
||||
Pill,
|
||||
Span,
|
||||
Template,
|
||||
)
|
||||
|
||||
# Both comboboxes are wired by ts/search_select.ts (compiled to dist/).
|
||||
_SEARCH_SELECT_MEDIA = Media(js=("dist/search_select.js",))
|
||||
@@ -58,17 +65,18 @@ LabeledOption = tuple[str, str]
|
||||
# with the search input. The options panel is absolute, so it sits outside the
|
||||
# flex flow. (border omitted intentionally — see if it's needed later.)
|
||||
# The widget owns its disabled appearance: when any control inside it is
|
||||
# :disabled (e.g. add_purchase.ts disabling the search input), the wrapper greys
|
||||
# itself via :has() — callers only toggle the control's `disabled`, never styles.
|
||||
# :disabled (e.g. add_purchase.ts disabling the search input), the wrapper fades
|
||||
# via :has() — the same opacity-50 a disabled native input uses (see
|
||||
# _DISABLED_CONTROL in games/forms.py), so the two look identical. Callers only
|
||||
# toggle the control's `disabled`, never styles.
|
||||
_CONTAINER_CLASS = (
|
||||
"relative flex flex-wrap items-center gap-1 p-2 "
|
||||
"rounded-base bg-neutral-secondary-medium "
|
||||
"has-[:disabled]:opacity-50 has-[:disabled]:cursor-not-allowed"
|
||||
f"rounded-base bg-neutral-secondary-medium {DISABLED_WITHIN_CLASS}"
|
||||
)
|
||||
_PILLS_CLASS = "contents"
|
||||
# disabled:cursor-not-allowed matches the wrapper's cursor so hovering across
|
||||
# the whole widget stays consistent — the inner input is excluded from the
|
||||
# global disabled rule (input.css), which would otherwise have set it.
|
||||
# the whole widget stays consistent (the wrapper handles the faded look via
|
||||
# has-[:disabled]:opacity-50).
|
||||
_SEARCH_CLASS = (
|
||||
"flex-1 min-w-[8rem] border-0 bg-transparent text-sm text-heading "
|
||||
"focus:ring-0 focus:outline-hidden placeholder:text-body "
|
||||
|
||||
Reference in New Issue
Block a user