@crisp-ui-kit/crisp 0.55.3 → 0.55.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -697,11 +697,19 @@
697
697
  & .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
698
698
  /* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
699
699
  & th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
700
- & th[data-sticky] { z-index: 3;
701
- } /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
702
- `tfoot td { z-index: 2 }
703
- `), which `td[data-sticky]`'s own higher-specificity
704
- `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
700
+ /* `thead th[data-sticky]`, not a bare `th[data-sticky]` (crisp#848, same
701
+ bare-`th` family as #829/#833/#835): this z-index:3 was written for
702
+ the HEADER's own sticky corner cell (sticky column x sticky header), meant to win over everything else. Unscoped, it also matched the primary column's BODY cell -- a real `<th scope="row" data-sticky>`
703
+ since #779 -- giving every scrolled body row's own sticky cell a HIGHER z-index than the sticky FOOTER's (2), so it painted over the
704
+ footer instead of the footer showing through. Confirmed live on a
705
+ real bounded-height, 25-row table (GRC's Risk register): scrolled partway, `elementFromPoint` at the footer's own on-screen cell
706
+ resolved to a body row's `<th>`, not the footer's `<td>`, and that
707
+ `<th>`'s computed z-index read 3. Scoped to `thead`, a body row's
708
+ own sticky `<th>` now falls back to the base rule above (z-index 1,
709
+ correct -- the same as a plain sticky `<td>`), and the footer's own explicit z-index below is unaffected either way. */
710
+ & thead th[data-sticky] { z-index: 3; }
711
+ /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
712
+ `tfoot td { z-index: 2 }`), which `td[data-sticky]`'s own higher-specificity `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
705
713
  & tfoot td[data-sticky] { z-index: 2; }
706
714
  & tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
707
715
  & tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
@@ -718,103 +726,101 @@
718
726
  & tbody tr[data-selected] td { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
719
727
  & tbody tr[data-selected]:hover td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface))); }
720
728
  & .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
721
- /* crisp/#758: AI-enriched column marker, ported from RecordsTable's own already-measured value -- a full-bleed #9b69ff (bgPurple) wash at 4%, composited OVER whatever row-state background the cell already has (hover, selected), which is why it's an ::after overlay and not a
722
- plain background. */
729
+ /* crisp/#758: AI-enriched column marker, ported from RecordsTable's own
730
+ already-measured value -- a full-bleed #9b69ff (bgPurple) wash at 4%,
731
+ composited OVER whatever row-state background the cell already has
732
+ (hover, selected), which is why it's an ::after overlay and not a plain background. */
723
733
  & td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, var(--crisp-bg-purple) 4%, transparent); }
724
734
  & .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
725
735
  & .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
726
- /* An async onEdit's in-flight state (#385) -- the field/picker dims and stops accepting input rather than disappearing, so the just-typed or just-chosen value stays visible while its commit is pending. Same treatment Input already gives its own disabled state. */
736
+ /* An async onEdit's in-flight state (#385) -- the field/picker dims and
737
+ stops accepting input rather than disappearing, so the just-typed or
738
+ just-chosen value stays visible while its commit is pending. Same
739
+ treatment Input already gives its own disabled state. */
727
740
  & .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
728
741
  & .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
729
- /* The rejection's message, beside the cell's own content -- a sibling in the TableCell, never nested inside a column's own render output, so a
730
- hover-only wrapper (a Tooltip, say) around a custom render can't hide it. Block-level: the row grows to fit it rather than floating over the row below, so it can never end up clipped by the scroll container's overflow the way a position:fixed escape would need
731
- guarding against. */
742
+ /* The rejection's message, beside the cell's own content -- a sibling in
743
+ the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
744
+ it. Block-level: the row grows to fit it rather than floating over
745
+ the row below, so it can never end up clipped by the scroll
746
+ container's overflow the way a position:fixed escape would need guarding against. */
732
747
  & .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
733
- /* editor: "select"/"switch" -- always live, no separate edit mode, so
734
- the cell IS the picker: same chromeless-fill treatment as
735
- RecordsTable's own --picker cells (measured there already; DataTable shares RecordsTable's grid, Golden Rule 0). */
748
+ /* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
749
+ shares RecordsTable's grid, Golden Rule 0). */
736
750
  & .crisp-datatable-td--picker { padding: 0; }
737
751
  & .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
738
752
  & .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
739
753
  & .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
740
- /* reference picker: a leading 16px avatar per option, plus a trailing
741
- "Create X" row when the consumer supplies one (#449) -- same measured
742
- values as RecordsTable's own .crisp-recordstable-refoption*. */
754
+ /* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
743
755
  & .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
744
756
  & .crisp-datatable-refoption-avatar { flex-shrink: 0; }
745
757
  & .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
746
758
  & .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
747
- /* #826 -- the full-body loading state (a filter/sort re-query, or a fresh page boot), measured live on Attio's Companies grid on both
748
- triggers: icon+text centred in the row-body area, 10px gap, 14px/500
749
- weight/20px line-height, both icon and text in the SAME muted
750
- rgba(0,0,0,0.63) -- the same color-mix figure this file already uses
751
- for other muted text (e.g. -phone-dial). Distinct from the cold
752
- in-place row load (blank, no indicator) and the warm-navigation
753
- per-cell shimmer (#599) -- neither of those swaps the whole body. */
759
+ /* #826 -- the full-body loading state (a filter/sort re-query, or a
760
+ fresh page boot), measured live on Attio's Companies grid on both triggers: icon+text centred in the row-body area, 10px gap, 14px/500 weight/20px line-height, both icon and text in the SAME muted rgba(0,0,0,0.63) -- the same color-mix figure this file already uses for other muted text (e.g. -phone-dial). Distinct from the cold in-place row load (blank, no indicator) and the warm-navigation per-cell shimmer (#599) -- neither of those swaps the whole body. */
754
761
  & .crisp-datatable-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: var(--crisp-space-8) var(--crisp-space-3); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); line-height: 20px; }
755
762
  & .crisp-datatable-loading .crisp-spinner { border-top-color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
756
- /* #579: click-to-expand overlay for a truncated cell -- a read-only,
757
- full-value card, settled live as click (not Attio's usual hover-expand) and measured position: absolute, unlike this file's
758
- floating chrome (Menu/Select popovers) which portals and escapes the
759
- scroll container. Deliberately NOT the edit-error pattern above
760
- (grow the row, block-level, never clipped): a virtualized grid
761
- assumes a fixed row height for its scroll math (owned by the grid
762
- core, not this cell), so growing a row here would fight it. Anchored
763
- to the SAME column width the cell itself renders at (measured:
764
- Attio's own overlay came out only slightly wider than the triggering cell's clientWidth -- the overshoot is this padding),
765
- wrapping onto as many lines as the value needs; it CAN be clipped by
766
- `.crisp-table-scroll`'s own `overflow: auto` if it grows past the visible viewport, same as the edit-error comment above already notes for any position:absolute escape -- unmeasured against Attio for this exact case, flagged rather than guessed. */
763
+ /* #579: click-to-expand overlay for a truncated cell -- a read-only, full-value card, settled live as click (not Attio's usual
764
+ hover-expand) and measured position: absolute, unlike this file's floating chrome (Menu/Select popovers) which portals and escapes the scroll container. Deliberately NOT the edit-error pattern above (grow the row, block-level, never clipped): a virtualized grid assumes a fixed row height for its scroll math (owned by the grid core, not this cell), so growing a row here would fight it. Anchored to the SAME column width the cell itself renders at (measured: Attio's own overlay came out only slightly wider than the
765
+ triggering cell's clientWidth -- the overshoot is this padding), wrapping onto as many lines as the value needs; it CAN be clipped by `.crisp-table-scroll`'s own `overflow: auto` if it grows past the
766
+ visible viewport, same as the edit-error comment above already
767
+ notes for any position:absolute escape -- unmeasured against Attio
768
+ for this exact case, flagged rather than guessed. */
767
769
  & td[data-cell-expanded] { overflow: visible; z-index: 7; }
768
- & .crisp-datatable-cell-overlay { position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box; padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface); border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default; }
770
+ & .crisp-datatable-cell-overlay {
771
+ position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
772
+ padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface);
773
+ border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px;
774
+ font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium);
775
+ color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default;
776
+ }
769
777
  /* fixed layout so column widths (and resize) are exact — like RecordsTable */
770
778
  & table { table-layout: fixed; }
771
- /* read-only/computed column marker (#596) -- sits just inside the resize handle, same absolute-positioned-against-the-th approach the handle itself uses, since th has zero padding and the button/inner row fills the whole cell with no room left for a trailing sibling. */
779
+ /* read-only/computed column marker (#596) -- sits just inside the
780
+ resize handle, same absolute-positioned-against-the-th approach the
781
+ handle itself uses, since th has zero padding and the button/inner
782
+ row fills the whole cell with no room left for a trailing sibling. */
772
783
  & .crisp-datatable-readonly { position: absolute; top: 50%; inset-inline-end: 11px; transform: translateY(-50%); color: var(--crisp-fg-muted); pointer-events: none; z-index: 5; }
773
784
  /* column resize grip */
774
785
  & .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
775
786
  & .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
776
787
  & .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
777
- /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
778
- own grid (the issue's own comment): the dragged header keeps full opacity and the plain `pointer` cursor throughout (Attio draws no ghost and no "grabbing" cursor of its own), so this adds nothing to `[data-dragging]` itself -- the ONLY new paint is the drop-indicator line, a 3px bar in the same measured brand blue the resize handle's
779
- own hover state (above) already uses, positioned on whichever edge of
780
- the hovered header the pointer is closer to. */
788
+ /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's own grid (the issue's own comment): the dragged header keeps full
789
+ opacity and the plain `pointer` cursor throughout (Attio draws no
790
+ ghost and no "grabbing" cursor of its own), so this adds nothing to
791
+ `[data-dragging]` itself -- the ONLY new paint is the drop-indicator
792
+ line, a 3px bar in the same measured brand blue the resize handle's own hover state (above) already uses, positioned on whichever edge of the hovered header the pointer is closer to. */
781
793
  & th[data-dragging] { cursor: pointer; }
782
- & th[data-drop-before]::before, & th[data-drop-after]::after {
783
- content: ""; position: absolute; top: 0; width: 3px; height: 100%;
784
- background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none;
785
- }
794
+ & th[data-drop-before]::before, & th[data-drop-after]::after { content: ""; position: absolute; top: 0; width: 3px; height: 100%; background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none; }
786
795
  & th[data-drop-before]::before { inset-inline-start: -1.5px; }
787
796
  & th[data-drop-after]::after { inset-inline-end: -1.5px; }
788
797
  /* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
789
798
  & .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
790
- /* crisp/#601 supersession follow-up -- the same flex-row shape
791
- .crisp-datatable-entity gives its own avatar+name+actions, but
792
- structural only (no entity-specific colour/weight): lets
793
- onOpenComments/onOpenPreview compose alongside ANY column's own content (a custom render, or a non-entity built-in type), not just type: "entity"'s own inline rendering. */
799
+ /* crisp/#601 supersession follow-up -- the same flex-row shape .crisp-datatable-entity gives its own avatar+name+actions, but structural only (no entity-specific colour/weight): lets onOpenComments/onOpenPreview compose alongside ANY column's own
800
+ content (a custom render, or a non-entity built-in type), not just
801
+ type: "entity"'s own inline rendering. */
794
802
  & .crisp-datatable-cell-actions-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; width: 100%; }
795
- /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
796
- plain hover vs rest) that Attio's underline is unconditional, not a hover reveal -- identical at rest and on hover: text-decoration-color is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text
797
- colour. The previous :hover-gated, full-contrast rule here was itself
798
- unmeasured and wrong in two ways at once. */
799
- & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
803
+ /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text, plain hover vs rest) that Attio's underline is unconditional, not a
804
+ hover reveal -- identical at rest and on hover: text-decoration-color
805
+ is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text colour. The previous :hover-gated, full-contrast rule here was itself unmeasured and wrong in two ways at once. */ /* crisp#851 -- Attio's own entity/record names are measured at
806
+ font-weight 500, distinct from a plain-text primary column's 400 (table.recipe.ts's own `tbody th` default). This was never its own
807
+ explicit style -- it silently rode on a since-fixed header-rule leak
808
+ (#833/#835/#848) that happened to apply the same 500 to every
809
+ primary-column body cell regardless of type. Made explicit here so
810
+ fixing that leak doesn't also (wrongly) drop entity names to 400. */
811
+ & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--crisp-weight-medium); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
800
812
  & button.crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
801
- /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
802
- column, revealed on ROW hover (the issue's own title says "row
803
- hover", and a live check confirmed hovering well past the name -- not just the icons' own hit target -- still reveals them) or
804
- keyboard focus. Measured live: border-radius 6px, icon colour
805
- rgba(0,0,0,0.55) at rest, darkening to the full text colour with a
806
- rgba(0,0,0,0.04) background on the icon's OWN hover -- both expressed as color-mix against fgPrimary so they stay correct in dark mode instead of a literal black that would invert. */
813
+ /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary column, revealed on ROW hover (the issue's own title says "row
814
+ hover", and a live check confirmed hovering well past the name --
815
+ not just the icons' own hit target -- still reveals them) or keyboard focus. Measured live: border-radius 6px, icon colour rgba(0,0,0,0.55) at rest, darkening to the full text colour with a rgba(0,0,0,0.04) background on the icon's OWN hover -- both
816
+ expressed as color-mix against fgPrimary so they stay correct in
817
+ dark mode instead of a literal black that would invert. */
807
818
  & .crisp-datatable-entity-actions { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
808
819
  & tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
809
820
  & .crisp-datatable-entity-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
810
821
  & .crisp-datatable-entity-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
811
822
  & .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
812
- & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
813
- & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
814
- & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
815
- & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
816
- & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
817
- & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
823
+ & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
818
824
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
819
825
  & .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
820
826
  & .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
@@ -827,58 +833,74 @@
827
833
  & .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
828
834
  & .crisp-datatable-num { font-variant-numeric: tabular-nums; }
829
835
  & .crisp-datatable-muted { color: var(--crisp-fg-muted); }
830
- /* crisp#822 -- a date/datetime cell's danger/overdue tone: same token as
831
- TasksTable's own overdue due-date (crisp#462), plain text colour, no fill/weight change. */
836
+ /* crisp#822 -- a date/datetime cell's danger/overdue tone: same token as TasksTable's own overdue due-date (crisp#462), plain text colour, no
837
+ fill/weight change. */
832
838
  & .crisp-datatable-danger { color: var(--crisp-fg-danger); }
833
- /* url/email cell types (#654) -- same measured values as RecordsTable's
834
- own .crisp-recordstable-link, which this mirrors: no underline at
835
- rest, underline on hover, own-colour text rather than a browser-blue
836
- link. */
839
+ /* url/email cell types (#654) -- same measured values as RecordsTable's own .crisp-recordstable-link, which this mirrors: no underline at rest, underline on hover, own-colour text rather than a browser-blue link. */
837
840
  & .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
838
841
  & .crisp-datatable-link:hover { text-decoration: underline; }
839
- /* phone cell type/editor (#765) -- measured live: the dial-code prefix
840
- is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text
841
- value already measured elsewhere in this file. */
842
+ /* phone cell type/editor (#765) -- measured live: the dial-code prefix is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text value already measured elsewhere in this file. */
842
843
  & .crisp-datatable-phone { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
843
844
  & .crisp-datatable-phone-dial { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
844
845
  & .crisp-datatable-phone-editor { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
845
846
  & .crisp-datatable-phone-editor input { flex: 1; min-width: 0; }
846
- /* country-picker option row (#842) -- confirmed live: Attio's own popover is plain text, no flag anywhere (not the option row, not the closed trigger), name opaque + dial code muted rgba(0,0,0,0.63) -- the SAME color-mix figure as -phone-dial above. The internal name/dial gap wasn't independently measured this pass (Attio's own two <div>s gave no exact number); reused space["1"], the same gap -phone already uses for its own flag/dial/digits row. */
847
+ /* country-picker option row (#842) -- confirmed live: Attio's own popover
848
+ is plain text, no flag anywhere (not the option row, not the closed
849
+ trigger), name opaque + dial code muted rgba(0,0,0,0.63) -- the SAME
850
+ color-mix figure as -phone-dial above. The internal name/dial gap
851
+ wasn't independently measured this pass (Attio's own two <div>s gave
852
+ no exact number); reused space["1"], the same gap -phone already uses
853
+ for its own flag/dial/digits row. */
847
854
  & .crisp-datatable-country-option { display: flex; align-items: center; gap: var(--crisp-space-1); }
848
855
  & .crisp-datatable-country-dial { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
849
856
  /* multi-value phone (#816) -- confirmed live (a real custom "Multiple
850
857
  values" Phone Number attribute, and the built-in People "Phone
851
- numbers" field): the collapsed cell is a 4px-gap flex row of real tel: links in a measured link blue (~rgb(59,108,232) pixel-sampled; reused as the closest already-tokened blue, brand600, rather than adding a third blue token for a delta this small), underlined, UNLIKE the single-value cell above (plain text, no link, no underline) -- multi-value is the one shape Attio makes into a real link. The cell hard-clips on overflow (no ellipsis -- confirmed live, a second value can end mid-digit with no "..."). */
858
+ numbers" field): the collapsed cell is a 4px-gap flex row of real
859
+ tel: links in a measured link blue (~rgb(59,108,232) pixel-sampled;
860
+ reused as the closest already-tokened blue, brand600, rather than
861
+ adding a third blue token for a delta this small), underlined,
862
+ UNLIKE the single-value cell above (plain text, no link, no
863
+ underline) -- multi-value is the one shape Attio makes into a real
864
+ link. The cell hard-clips on overflow (no ellipsis -- confirmed
865
+ live, a second value can end mid-digit with no "..."). */
852
866
  & .crisp-datatable-phone-multi { display: inline-flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
853
867
  & .crisp-datatable-phone-link { color: var(--crisp-bg-brand-solid); text-decoration: underline; white-space: nowrap; }
854
- /* multi-value editor (#816) -- confirmed live: a stack of committed- value rows (32px = size.menuRow, 5px 8px padding, 8px gap between the grip/value/actions, 16px icons) above one ALWAYS-blank trailing DataPhoneEditor-shaped row -- that blank row is the only "add
855
- another" affordance, there's no separate button for it. The
856
- copy/delete pair reuses .crisp-datatable-entity-action's own hover-reveal chrome (not independently re-measured as its own hit target this pass, only the 16px icon glyphs were). */
868
+ /* multi-value editor (#816) -- confirmed live: a stack of committed-
869
+ value rows (32px = size.menuRow, 5px 8px padding, 8px gap between
870
+ the grip/value/actions, 16px icons) above one ALWAYS-blank trailing
871
+ DataPhoneEditor-shaped row -- that blank row is the only "add
872
+ another" affordance, there's no separate button for it. The copy/delete pair reuses .crisp-datatable-entity-action's own
873
+ hover-reveal chrome (not independently re-measured as its own hit
874
+ target this pass, only the 16px icon glyphs were). */
857
875
  & .crisp-datatable-phone-multi-editor { display: flex; flex-direction: column; }
858
876
  & .crisp-datatable-phone-multi-row { display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); padding: 5px var(--crisp-space-2); }
859
877
  & .crisp-datatable-phone-multi-row .crisp-datatable-phone-link { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
860
878
  & .crisp-datatable-phone-multi-grip { color: var(--crisp-fg-muted); flex-shrink: 0; cursor: grab; }
861
879
  & .crisp-datatable-phone-multi-actions { display: flex; align-items: center; gap: var(--crisp-space-1); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
862
- & .crisp-datatable-phone-multi-row:hover .crisp-datatable-phone-multi-actions, & .crisp-datatable-phone-multi-actions:focus-within { opacity: 1; }
880
+ & .crisp-datatable-phone-multi-row:hover .crisp-datatable-phone-multi-actions,
881
+ & .crisp-datatable-phone-multi-actions:focus-within { opacity: 1; }
863
882
  & .crisp-datatable-phone-multi-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
864
883
  & .crisp-datatable-phone-multi-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
865
884
  /* datetime cell editor (#762) -- DatePickerField + TimePicker side by side. */
866
885
  & .crisp-datatable-datetime-editor { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
867
- /* rating cell type/editor (#760) -- measured live: 16px stars on an 18px pitch (a 2px gap), an empty star at rgba(0,0,0,0.1), a committed/filled star at brand600 (--crisp-bg-brand-solid, an exact match to the measured #266df0), and a distinct hover-preview colour -- rgb(33,91,196) -- that's close to but NOT brand700/bgBrandSolidPressed
868
- (#1f5fd6); kept as its own literal rather than forced onto that token,
869
- since the two were measured stable and different. The read-only svg
870
- and the editable button's own inner svg both carry the crisp-datatable-rating-star class on the svg itself (not the button), so one set of fill rules covers both -- the button only adds its own reset + cursor via a separate -btn class. */
886
+ /* rating cell type/editor (#760) -- measured live: 16px stars on an
887
+ 18px pitch (a 2px gap), an empty star at rgba(0,0,0,0.1), a
888
+ committed/filled star at brand600 (--crisp-bg-brand-solid, an exact
889
+ match to the measured #266df0), and a distinct hover-preview colour
890
+ -- rgb(33,91,196) -- that's close to but NOT brand700/bgBrandSolidPressed (#1f5fd6); kept as its own literal rather than forced onto that token, since the two were measured stable and different. The read-only svg and the editable button's own inner svg both carry the
891
+ crisp-datatable-rating-star class on the svg itself (not the
892
+ button), so one set of fill rules covers both -- the button only
893
+ adds its own reset + cursor via a separate -btn class. */
871
894
  & .crisp-datatable-rating { display: inline-flex; align-items: center; gap: 2px; }
872
895
  & .crisp-datatable-rating-star { display: block; }
873
896
  & .crisp-datatable-rating-star path { fill: rgba(0, 0, 0, 0.1); }
874
897
  & .crisp-datatable-rating-star[data-filled] path { fill: var(--crisp-bg-brand-solid); }
875
898
  & .crisp-datatable-rating-star[data-hovering] path { fill: #215bc4; }
876
899
  & .crisp-datatable-rating-star-btn { display: inline-flex; padding: 0; margin: 0; border: none; background: none; font-family: inherit; cursor: pointer; }
877
- /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures a leading 20px grip column, a 16px grip icon, and the row's own
878
- hairline doubling as the drop indicator (it tints toward the brand
879
- colour as the active drop target). The affordance + geometry are
880
- measured; the drag MECHANISM is crisp's existing house idiom -- HTML5 draggable + dataTransfer, the same one column reorder already uses (RecordsTable's reorderColumn) -- not a proprietary
881
- pointer-events implementation. */
900
+ /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
901
+ a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
902
+ draggable + dataTransfer, the same one column reorder already uses
903
+ (RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
882
904
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
883
905
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
884
906
  & .crisp-datatable-grip:active { cursor: grabbing; }
@@ -1886,6 +1908,18 @@
1886
1908
  row's own grid line. `thead th` simply never matches `tbody th[scope=row]` at all, so none of this reaches it; the base `& th, & td` rule above (36px, one border-bottom, full opacity via `& table`'s own color) applies to it like every other cell. */
1887
1909
  & thead th { position: sticky; top: 0; z-index: 2; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: var(--crisp-fg-primary); user-select: none; }
1888
1910
  & td { color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); }
1911
+ /* crisp#851 -- a REGRESSION from #833/#835/#848's own fix: scoping the header rule above to `thead th` correctly stopped it leaking onto the primary column's body cell (a real `<th scope="row">` since
1912
+ #779), but that same leaked rule's `font-weight: var(--crisp-weight-medium)` was the ONLY thing overriding the browser's own UA stylesheet default
1913
+ for `<th>` (`font-weight: bold; text-align: center`) on that cell --
1914
+ once the leak stopped, a plain-text primary column fell through to
1915
+ bold+centred instead of matching its sibling `<td>`s (`400`/start,
1916
+ Attio's own plain-text primary cell). `text-align` was already safe (the base `& th, & td` rule's own `text-align: start` above is
1917
+ author-origin and always beats the UA stylesheet regardless of
1918
+ specificity) but is restated here explicitly, defensively -- cheap,
1919
+ and removes any doubt. `type: "entity"` primary columns are a
1920
+ separate case (Attio's own entity/record names are 500, not 400) -- see `.crisp-datatable-entity-name`'s own new explicit weight in
1921
+ datatable.recipe.ts, which wins here by class specificity. */
1922
+ & tbody th { font-weight: var(--crisp-weight-regular); text-align: start; }
1889
1923
  & th:last-child, & td:last-child { border-inline-end: none; }
1890
1924
  & tbody tr:last-child td { border-bottom: none; }
1891
1925
  /* #554: sticky to the bottom, mirroring `.crisp-recordstable-sum`. */
package/dist/table.css CHANGED
@@ -23,6 +23,18 @@
23
23
  row's own grid line. `thead th` simply never matches `tbody th[scope=row]` at all, so none of this reaches it; the base `& th, & td` rule above (36px, one border-bottom, full opacity via `& table`'s own color) applies to it like every other cell. */
24
24
  & thead th { position: sticky; top: 0; z-index: 2; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: var(--crisp-fg-primary); user-select: none; }
25
25
  & td { color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); }
26
+ /* crisp#851 -- a REGRESSION from #833/#835/#848's own fix: scoping the header rule above to `thead th` correctly stopped it leaking onto the primary column's body cell (a real `<th scope="row">` since
27
+ #779), but that same leaked rule's `font-weight: var(--crisp-weight-medium)` was the ONLY thing overriding the browser's own UA stylesheet default
28
+ for `<th>` (`font-weight: bold; text-align: center`) on that cell --
29
+ once the leak stopped, a plain-text primary column fell through to
30
+ bold+centred instead of matching its sibling `<td>`s (`400`/start,
31
+ Attio's own plain-text primary cell). `text-align` was already safe (the base `& th, & td` rule's own `text-align: start` above is
32
+ author-origin and always beats the UA stylesheet regardless of
33
+ specificity) but is restated here explicitly, defensively -- cheap,
34
+ and removes any doubt. `type: "entity"` primary columns are a
35
+ separate case (Attio's own entity/record names are 500, not 400) -- see `.crisp-datatable-entity-name`'s own new explicit weight in
36
+ datatable.recipe.ts, which wins here by class specificity. */
37
+ & tbody th { font-weight: var(--crisp-weight-regular); text-align: start; }
26
38
  & th:last-child, & td:last-child { border-inline-end: none; }
27
39
  & tbody tr:last-child td { border-bottom: none; }
28
40
  /* #554: sticky to the bottom, mirroring `.crisp-recordstable-sum`. */
@@ -24,6 +24,18 @@
24
24
  row's own grid line. `thead th` simply never matches `tbody th[scope=row]` at all, so none of this reaches it; the base `& th, & td` rule above (36px, one border-bottom, full opacity via `& table`'s own color) applies to it like every other cell. */
25
25
  & thead th { position: sticky; top: 0; z-index: 2; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: var(--crisp-fg-primary); user-select: none; }
26
26
  & td { color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); }
27
+ /* crisp#851 -- a REGRESSION from #833/#835/#848's own fix: scoping the header rule above to `thead th` correctly stopped it leaking onto the primary column's body cell (a real `<th scope="row">` since
28
+ #779), but that same leaked rule's `font-weight: var(--crisp-weight-medium)` was the ONLY thing overriding the browser's own UA stylesheet default
29
+ for `<th>` (`font-weight: bold; text-align: center`) on that cell --
30
+ once the leak stopped, a plain-text primary column fell through to
31
+ bold+centred instead of matching its sibling `<td>`s (`400`/start,
32
+ Attio's own plain-text primary cell). `text-align` was already safe (the base `& th, & td` rule's own `text-align: start` above is
33
+ author-origin and always beats the UA stylesheet regardless of
34
+ specificity) but is restated here explicitly, defensively -- cheap,
35
+ and removes any doubt. `type: "entity"` primary columns are a
36
+ separate case (Attio's own entity/record names are 500, not 400) -- see `.crisp-datatable-entity-name`'s own new explicit weight in
37
+ datatable.recipe.ts, which wins here by class specificity. */
38
+ & tbody th { font-weight: var(--crisp-weight-regular); text-align: start; }
27
39
  & th:last-child, & td:last-child { border-inline-end: none; }
28
40
  & tbody tr:last-child td { border-bottom: none; }
29
41
  /* #554: sticky to the bottom, mirroring `.crisp-recordstable-sum`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.55.3",
3
+ "version": "0.55.5",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",