From 104f34fee077f1ee15c2dfe2fab92641ddde91fc Mon Sep 17 00:00:00 2001 From: Felix Thape <67233923+DasProffi@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:47:24 +0200 Subject: [PATCH 01/25] chore: change data-name to className --- conventions/Components.md | 66 +++++++++++++ conventions/Interaction and Feedback.md | 99 +++++++++++++++++++ .../display-and-visualization/Avatar.tsx | 30 +++--- .../display-and-visualization/Chip.tsx | 11 ++- .../ExpansionIcon.tsx | 6 +- .../ProcessModelActivityNode.tsx | 3 +- .../process-model/ProcessModelCanvas.tsx | 4 +- .../process-model/ProcessModelTraceReplay.tsx | 3 +- .../src/components/form/FieldLayout.tsx | 17 +--- .../src/components/layout/Expandable.tsx | 33 +++---- .../src/components/layout/TabSwitcher.tsx | 19 ++-- .../src/components/layout/app/AppPage.tsx | 22 ++--- .../src/components/layout/dialog/Dialog.tsx | 14 +-- .../layout/drawer/DrawerContainer.tsx | 8 +- .../layout/navigation/BreadCrumbs.tsx | 7 +- .../VerticalNavigationMenu.tsx | 3 +- .../src/components/layout/popup/PopUp.tsx | 21 ++-- .../components/layout/table/FillerCell.tsx | 9 +- .../src/components/layout/table/TableBody.tsx | 7 +- .../src/components/layout/table/TableCell.tsx | 3 +- .../components/layout/table/TableDisplay.tsx | 18 ++-- .../components/layout/table/TableHeader.tsx | 11 +-- .../layout/table/VirtualizedTableBody.tsx | 11 +-- .../layout/table/useNaturalColumnWidthLock.ts | 2 +- .../virtualization/virtualizationScroll.ts | 2 +- .../components/user-interaction/Button.tsx | 8 +- .../components/user-interaction/Checkbox.tsx | 12 +-- .../Combobox/ComboboxList.tsx | 6 +- .../Combobox/ComboboxOption.tsx | 10 +- .../FileInput/FileInputMenu.tsx | 13 +-- .../FileInput/FileInputTrigger.tsx | 14 +-- .../MultiSelectChipDisplayTrigger.tsx | 13 ++- .../MultiSelect/MultiSelectContent.tsx | 6 +- .../MultiSelect/MultiSelectOption.tsx | 5 +- .../MultiSelect/MultiSelectTrigger.tsx | 15 ++- .../user-interaction/Select/SelectContent.tsx | 3 +- .../user-interaction/Select/SelectOption.tsx | 5 +- .../user-interaction/Select/SelectTrigger.tsx | 15 ++- .../components/user-interaction/Switch.tsx | 11 +-- .../components/user-interaction/Textarea.tsx | 9 +- .../components/user-interaction/Tooltip.tsx | 22 ++--- .../user-interaction/data/FilterPopUp.tsx | 5 +- .../user-interaction/date/DayPicker.tsx | 18 ++-- .../user-interaction/date/TimeInput.tsx | 17 ++-- .../user-interaction/date/TimePicker.tsx | 15 +-- .../user-interaction/input/DateTimeField.tsx | 15 ++- .../user-interaction/input/DateTimeInput.tsx | 8 +- .../user-interaction/input/Input.tsx | 14 +-- .../user-interaction/input/NumberInput.tsx | 8 +- .../input/NumberStepperInput.tsx | 8 +- .../input/ToggleableInput.tsx | 3 +- .../properties/DateProperty.tsx | 3 +- .../properties/MultiSelectProperty.tsx | 2 +- .../properties/NumberProperty.tsx | 11 +-- .../properties/PropertyBase.tsx | 17 ++-- .../properties/SelectProperty.tsx | 5 +- .../properties/TextProperty.tsx | 3 +- .../src/style/theme/components/app-page.css | 16 +-- .../src/style/theme/components/avatar.css | 14 +-- .../src/style/theme/components/breadcrumb.css | 6 +- .../src/style/theme/components/button.css | 4 +- .../components/chat/chat-message-card.css | 2 +- .../src/style/theme/components/checkbox.css | 4 +- .../src/style/theme/components/chip.css | 4 +- .../src/style/theme/components/combobox.css | 10 +- .../theme/components/date-time-input.css | 8 +- .../src/style/theme/components/day-picker.css | 10 +- .../src/style/theme/components/dialog.css | 6 +- .../src/style/theme/components/drawer.css | 2 +- .../src/style/theme/components/expandable.css | 6 +- .../style/theme/components/expansion-icon.css | 2 +- .../src/style/theme/components/file-input.css | 20 ++-- .../src/style/theme/components/form-field.css | 8 +- .../style/theme/components/input-elements.css | 2 +- .../src/style/theme/components/pop-up.css | 2 +- .../src/style/theme/components/property.css | 44 ++++----- .../src/style/theme/components/select.css | 20 ++-- .../src/style/theme/components/switch.css | 6 +- .../src/style/theme/components/table.css | 26 ++--- .../style/theme/components/tabswitcher.css | 8 +- .../src/style/theme/components/textarea.css | 2 +- .../style/theme/components/time-picker.css | 4 +- .../theme/components/toggleable-intput.css | 2 +- .../src/style/theme/components/tooltip.css | 4 +- .../theme/components/vertical-navigation.css | 2 +- packages/hightide-web/src/utils/propsUtil.ts | 8 +- .../tests/table/naturalColumnSizing.test.tsx | 6 +- .../virtualizationScroll.test.ts | 4 +- 88 files changed, 551 insertions(+), 459 deletions(-) create mode 100644 conventions/Components.md create mode 100644 conventions/Interaction and Feedback.md diff --git a/conventions/Components.md b/conventions/Components.md new file mode 100644 index 00000000..d675577b --- /dev/null +++ b/conventions/Components.md @@ -0,0 +1,66 @@ +# Components + +How components are structured. + +## Anatomy + +Every component is split into primitives, and every component has an aggregate. + +The aggregate is the component used in the common case. Primitives are the individual elements of that component, attached as static members of the aggregate. + +```tsx + +``` + +` + A + +``` + +`value` and `onValueChange` parameterize `Select.Root`. `placeholder` parameterizes `Select.Trigger`. `triggerProps` and `contentProps` are applied on top of the props the aggregate already passed, so they win when both set the same prop. + +That aggregate is the same composition as: + +```tsx + + + + A + + +``` + +### Primitives + +A primitive owns one element: the root, the trigger, the content panel, an option, and so on. Name them `Component.Element`, and export them on the aggregate. + +### Root + +When primitives share state, the component provides a `Component.Root` primitive. Root parameterizes that state and provides it through context. The other primitives read the context. They do not own the shared state themselves. + +`Select.Root` holds the open state, the current value, interaction flags such as disabled, invalid, and read-only, and option registration, and passes them through `Select.Context`. + +A component whose primitives do not share state does not have a Root. + +## Styling identification + +Components identify themselves for styling with class names. Do not use `data-name`, or any other data attribute, as the styling hook. + +Data attributes are reserved for states and configuration, for example `data-disabled`, `data-invalid`, and `data-processing="subtle"`. diff --git a/conventions/Interaction and Feedback.md b/conventions/Interaction and Feedback.md new file mode 100644 index 00000000..a31477c0 --- /dev/null +++ b/conventions/Interaction and Feedback.md @@ -0,0 +1,99 @@ +# Interaction and Feedback + +Minimum states an element must differentiate. These apply to mobile and web unless a state is marked web only. + +There are three component types: Presentation, Interactive, and DataInput. Each type lists its own states. States are not inherited from another type. + +LifeCycle is separate from these types. It describes whether the element is available, waiting on data, blocked by an external operation or dependency, or performing an operation. + +## Presentation + +Presentation elements display content. They do not take input. + +Presentation elements have no interaction states. + +## Interactive + +Interactive elements can be activated. + +| State | Scope | +| --- | --- | +| `hover` | web only | +| `pressed` | mobile and web | +| `focus` | mobile and web | +| `focus-visible` | web only, often replacing `focus` | +| `disabled` | mobile and web | + +## DataInput + +Data input elements accept or display an editable value. + +| State | Scope | +| --- | --- | +| `readonly` | data input only | +| `invalid` | data input only | +| `hover` | web only | +| `pressed` | mobile and web | +| `focus` | mobile and web | +| `focus-visible` | web only, often replacing `focus` | +| `disabled` | mobile and web | + +## LifeCycle + +Every element can be in one lifecycle state: + +```ts +type LifeCycle = 'idle' | 'loading' | 'blocked' | 'processing' +``` + +### idle + +The element is available to be used. + +### loading + +The element is loading data. Its content is not ready to show yet. + +Show a skeleton or a placeholder in its place. The placeholder is often a rounded rectangle that roughly approximates the element's size. + +### blocked + +An external operation or dependency is preventing this element from being used. + +### processing + +This element is updating, or performing an operation, and that operation stops interaction. The element stays visible while the operation runs. + +## Interaction states + +`readonly`, `invalid`, `hover`, `pressed`, `focus`, `focus-visible`, and `disabled` follow the matching HTML semantics. + +### hover + +Web only. The pointer is over the element. This matches the CSS `:hover` pseudo-class. Mobile does not use this state. + +### pressed + +The element is being activated by a pointer or touch press. This matches `:active`. + +### focus + +The element has focus. This matches `:focus`. + +### focus-visible + +Web only. The element has focus and that focus should be shown, typically because it came from the keyboard. This matches `:focus-visible`. + +On web, `focus-visible` often replaces the `focus` visual, so pointer focus does not draw a focus ring. Mobile does not use this state. + +### disabled + +The element cannot be interacted with. This matches the `disabled` attribute and `:disabled`. + +### readonly + +Data input only. The value can be read and is not editable. This matches the `readonly` attribute. + +### invalid + +Data input only. The current value fails validation. This matches `:invalid` and `aria-invalid`. diff --git a/packages/hightide-web/src/components/display-and-visualization/Avatar.tsx b/packages/hightide-web/src/components/display-and-visualization/Avatar.tsx index 61ded924..320b411f 100644 --- a/packages/hightide-web/src/components/display-and-visualization/Avatar.tsx +++ b/packages/hightide-web/src/components/display-and-visualization/Avatar.tsx @@ -59,16 +59,14 @@ export const Avatar = ({ return (
+ className={clsx('avatar', props.className)} + data-size={size ?? undefined}> {image?.alt} setHasLoaded(true)} onError={() => setHasError(true)} data-error={hasError ? '' : undefined} @@ -81,11 +79,10 @@ export const Avatar = ({ } export type AvatarGroupProps = HTMLAttributes & { - 'avatars': Omit[], - 'showTotalNumber'?: boolean, - 'size'?: AvatarSize, - 'data-name'?: string, - 'ImageComponent'?: ElementType, + avatars: Omit[], + showTotalNumber?: boolean, + size?: AvatarSize, + ImageComponent?: ElementType, } /** @@ -118,14 +115,12 @@ export const AvatarGroup = ({ return (
+ className={clsx('avatar-group', props.className)} + data-size={size ?? undefined}> {group} {showTotalNumber && notDisplayedProfiles > 0 && ( {`+ ${notDisplayedProfiles}`} @@ -149,13 +144,12 @@ export const AvatarWithStatus = ({ }: AvatarWithStatusProps) => { return (
diff --git a/packages/hightide-web/src/components/display-and-visualization/Chip.tsx b/packages/hightide-web/src/components/display-and-visualization/Chip.tsx index d63e24ae..e4b79cc3 100644 --- a/packages/hightide-web/src/components/display-and-visualization/Chip.tsx +++ b/packages/hightide-web/src/components/display-and-visualization/Chip.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx' import type { HTMLAttributes } from 'react' import { ButtonUtil } from '../user-interaction/Button' @@ -31,11 +32,10 @@ export const Chip = ({ return (
+ data-size={size ?? undefined}> {children}
) @@ -53,7 +53,10 @@ export const ChipList = ({ ...props }: ChipListProps) => { return ( -
    +
      {list.map((value, index) => (
    • + data-disabled={disabled ? '' : undefined}> {children ? ( children ) : ( diff --git a/packages/hightide-web/src/components/display-and-visualization/process-model/ProcessModelActivityNode.tsx b/packages/hightide-web/src/components/display-and-visualization/process-model/ProcessModelActivityNode.tsx index c1557ac1..2c10fc9e 100644 --- a/packages/hightide-web/src/components/display-and-visualization/process-model/ProcessModelActivityNode.tsx +++ b/packages/hightide-web/src/components/display-and-visualization/process-model/ProcessModelActivityNode.tsx @@ -37,9 +37,8 @@ export const ProcessModelActivityNode = ({ const rootName = kind === 'terminal' ? 'process-model-terminal-node' : 'process-model-activity-node' return (
      +