From 9a3e68e724bc7cd9b5e62a773e535c152e9d2aa1 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Wed, 30 Sep 2026 21:55:23 -0700 Subject: [PATCH 1/3] Table and lightswitch styling fixes - AdminTable's inline padding is now opt-in via `admin-table--padded`, which the full-width admin tables now use - `.cp-table` cells get block padding and row borders between rows only, and lightswitch cells keep their padding - Editable tables' header cells inherit the table's alignment instead of the browser's centered `th` default - DataTable header cells with `text-center` now center their content, and the sort-icon spacer only renders for sortable columns - The site settings table's Homepage column header is centered - `craft-switch` adds `form-field__group-two--switch` to its control group, so it's only as tall as the switch - `craft-switch-button`'s track no longer has a -1px inline margin, and the indeterminate thumb is now centered Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + .../components/switch-button/switch-button.ts | 11 ++------ .../components/switch/switch.browser.test.ts | 28 +++++++++++++++++++ .../src/components/switch/switch.styles.ts | 7 +++++ .../src/components/switch/switch.ts | 9 ++++++ .../craftcms-ui/src/styles/shared/base.css | 28 +++++++++++++++---- resources/js/common/components/DataTable.vue | 5 ++-- .../admin-table/components/AdminTable.vue | 10 ++++--- .../sections/components/SiteSettingsTable.vue | 5 ++-- resources/js/pages/graphql/schemas/Index.vue | 2 +- resources/js/pages/graphql/tokens/Index.vue | 2 +- resources/js/pages/settings/assets/Index.vue | 1 + .../settings/assets/transformers/Index.vue | 2 +- .../settings/assets/transforms/Index.vue | 2 +- .../js/pages/settings/entry-types/Index.vue | 1 + resources/js/pages/settings/fields/Index.vue | 1 + .../js/pages/settings/sections/Index.vue | 1 + resources/js/pages/settings/sites/Index.vue | 1 + .../js/pages/settings/users/groups/Index.vue | 2 +- .../js/pages/settings/workflows/Index.vue | 1 + 20 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 packages/craftcms-ui/src/components/switch/switch.browser.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c8fb48c58f9..16c15aa0db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Replaced core inline element editing inputs with Form API controls rendered by Vue, with plugin field HTML compatibility handled by the Yii adapter. ([#19590](https://github.com/craftcms/cms/pull/19590)) - Improved the styling for collapsible field groups. - Improved the layout and dark mode styling of user edit screens. ([#19765](https://github.com/craftcms/cms/pull/19765)) +- Improved the styling of control panel tables and lightswitches. - Improved disabled secondary form actions with explanatory tooltips. - The “Assets” system settings nav item is now listed under “Content”, and “Globals”, “Categories”, and “Tags” are now listed under a new “Deprecated” heading, if those concepts are supported for the project. ([#19670](https://github.com/craftcms/cms/pull/19670)) - Added the `` web component. ([#19629](https://github.com/craftcms/cms/pull/19629)) diff --git a/packages/craftcms-ui/src/components/switch-button/switch-button.ts b/packages/craftcms-ui/src/components/switch-button/switch-button.ts index 0ae81f0608a..3c837b57018 100644 --- a/packages/craftcms-ui/src/components/switch-button/switch-button.ts +++ b/packages/craftcms-ui/src/components/switch-button/switch-button.ts @@ -58,7 +58,6 @@ export default class CraftSwitchButton extends LionSwitchButton { .switch-button__track { ${baseFormControlStyles} --tw-inset-shadow-color: var(--color-slate-300); - margin-inline: -1px; background-color: var(--c-color-neutral-fill-quiet); border-radius: var(--c-radius-full); min-height: unset; @@ -72,15 +71,13 @@ export default class CraftSwitchButton extends LionSwitchButton { border: 1px solid var(--c-form-control-border-color); background-color: var(--c-switch-thumb-fill, var(--c-surface-raised)); inset-block-start: calc(var(--c-switch-thumb-offset) / 2); - inset-inline-start: calc(var(--c-switch-thumb-offset) / 2 - 1px); + inset-inline-start: calc(var(--c-switch-thumb-offset) / 2); inset-inline-end: auto; box-sizing: border-box; } :host([indeterminate]:not([checked])) .switch-button__thumb { - inset-inline-start: calc( - 50% - (var(--c-switch-thumb-height) / 2) + 1px - ); + inset-inline-start: calc(50% - (var(--c-switch-thumb-height) / 2)); inset-inline-end: auto; } @@ -91,9 +88,7 @@ export default class CraftSwitchButton extends LionSwitchButton { :host([checked]) .switch-button__thumb { border: 1px solid var(--c-color-success-border-loud); inset-inline-start: auto; - inset-inline-end: calc( - (var(--c-switch-height) - var(--c-switch-thumb-height)) / 2 + 1px - ); + inset-inline-end: calc(var(--c-switch-thumb-offset) / 2); } :host([checked]) .switch-button__thumb:after { diff --git a/packages/craftcms-ui/src/components/switch/switch.browser.test.ts b/packages/craftcms-ui/src/components/switch/switch.browser.test.ts new file mode 100644 index 00000000000..092560d971f --- /dev/null +++ b/packages/craftcms-ui/src/components/switch/switch.browser.test.ts @@ -0,0 +1,28 @@ +import {beforeEach, expect, it} from 'vite-plus/test'; +import type CraftSwitch from './switch.js'; +import './switch.js'; + +beforeEach(() => { + document.body.innerHTML = ''; +}); + +it('is only as tall as its switch', async () => { + await import('../../styles/shared/color-palette.css'); + await import('../../styles/shared/colorable.css'); + await import('../../styles/shared/variables.css'); + await import('../../styles/shared/tokens.css'); + + for (const size of ['small', 'medium']) { + document.body.innerHTML = ``; + const element = document.querySelector('craft-switch') as CraftSwitch; + await element.updateComplete; + + const group = element.shadowRoot!.querySelector('.form-field__group-two')!; + const button = element.querySelector('craft-switch-button')!; + + const height = button.getBoundingClientRect().height; + + expect(height, size).toBeGreaterThan(0); + expect(group.getBoundingClientRect().height, size).toBe(height); + } +}); diff --git a/packages/craftcms-ui/src/components/switch/switch.styles.ts b/packages/craftcms-ui/src/components/switch/switch.styles.ts index 06b4eb5b3c0..8fcb633a0af 100644 --- a/packages/craftcms-ui/src/components/switch/switch.styles.ts +++ b/packages/craftcms-ui/src/components/switch/switch.styles.ts @@ -4,6 +4,13 @@ export default css` display: grid; } + .form-field__group-two--switch { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + } + .input-group { display: inline-flex; align-items: center; diff --git a/packages/craftcms-ui/src/components/switch/switch.ts b/packages/craftcms-ui/src/components/switch/switch.ts index 3ae6cbb671a..18dd62dc4da 100644 --- a/packages/craftcms-ui/src/components/switch/switch.ts +++ b/packages/craftcms-ui/src/components/switch/switch.ts @@ -535,6 +535,15 @@ export default class CraftSwitch extends HasLabel(LionSwitch) { } } + override render() { + return html` +
${this._groupOneTemplate()}
+
+ ${this._groupTwoTemplate()} +
+ `; + } + protected override _groupOneTemplate() { return html`${super._groupOneTemplate()} ${this._stateDescriptionTemplate()}`; } diff --git a/packages/craftcms-ui/src/styles/shared/base.css b/packages/craftcms-ui/src/styles/shared/base.css index 55235ca9263..e1ba38a6ec0 100644 --- a/packages/craftcms-ui/src/styles/shared/base.css +++ b/packages/craftcms-ui/src/styles/shared/base.css @@ -186,7 +186,7 @@ Modify with `cp-table--auto` to apply table-layout: auto */ .cp-table { --_cell-spacing-inline: var(--c-spacing-md); - --_cell-spacing-block: 0; + --_cell-spacing-block: var(--c-spacing-sm); --_row-height: calc(34rem / 16); text-align: start; width: 100%; @@ -202,6 +202,19 @@ Modify with `cp-table--auto` to apply table-layout: auto background-color: var(--c-surface-default); } + thead, + tbody, + tfoot { + &:not(:last-child) tr, + &:last-child tr:not(:last-child) { + th, + td { + border-block-end: 1px solid + color-mix(var(--c-color-neutral-border-quiet) 60%, transparent); + } + } + } + th, td { padding-block: var(--_cell-spacing-block); @@ -211,10 +224,12 @@ Modify with `cp-table--auto` to apply table-layout: auto overflow: hidden; text-overflow: ellipsis; width: 0; - border-block-end: 1px solid - color-mix(var(--c-color-neutral-border-quiet) 60%, transparent); - &:has(.cp-table-input:not([type='checkbox']):not([type='radio'])) { + &:has( + .cp-table-input:not([type='checkbox']):not([type='radio']):not( + craft-switch + ) + ) { padding: 0; } } @@ -260,6 +275,7 @@ Modify with `cp-table--auto` to apply table-layout: auto table-layout: auto; border: 1px solid var(--c-color-border-quiet); border-radius: var(--c-radius-sm); + margin-block-end: var(--c-spacing-md); thead, th { @@ -268,7 +284,9 @@ Modify with `cp-table--auto` to apply table-layout: auto th { font-size: var(--c-text-sm); - padding: var(--c-spacing-sm); + padding-block: var(--c-spacing-sm); + padding-inline: var(--c-spacing-md); + text-align: inherit; } td { diff --git a/resources/js/common/components/DataTable.vue b/resources/js/common/components/DataTable.vue index 69b047709ff..21b0747a557 100644 --- a/resources/js/common/components/DataTable.vue +++ b/resources/js/common/components/DataTable.vue @@ -288,7 +288,7 @@ :aria-sort="getAriaSortAttribute(header.column)" >
  props.disabled, meta: { trackSize: '80px', - cellClass: 'bg-[var(--c-color-neutral-fill-quiet)]', }, label: t('Enabled'), }), @@ -86,7 +85,7 @@ meta: { trackSize: '44px', cellClass: 'text-center', - headerClass: 'justify-center', + headerClass: 'text-center', }, onChange: (value, {row}) => { if (value) { @@ -148,7 +147,7 @@ fieldset: true, label: t('Route'), 'label-sr-only': true, - class: 'min-w-72 p-1', + class: 'min-w-72', }, [ h( diff --git a/resources/js/pages/graphql/schemas/Index.vue b/resources/js/pages/graphql/schemas/Index.vue index 75f39db0c45..7fd601e2d0e 100644 --- a/resources/js/pages/graphql/schemas/Index.vue +++ b/resources/js/pages/graphql/schemas/Index.vue @@ -89,5 +89,5 @@ t('New schema') }} - + diff --git a/resources/js/pages/graphql/tokens/Index.vue b/resources/js/pages/graphql/tokens/Index.vue index 08de12cb469..6cff2777101 100644 --- a/resources/js/pages/graphql/tokens/Index.vue +++ b/resources/js/pages/graphql/tokens/Index.vue @@ -94,7 +94,7 @@ t('New token') }} - + diff --git a/resources/js/pages/settings/assets/transforms/Index.vue b/resources/js/pages/settings/assets/transforms/Index.vue index 847b3fea5c3..d09407a5c06 100644 --- a/resources/js/pages/settings/assets/transforms/Index.vue +++ b/resources/js/pages/settings/assets/transforms/Index.vue @@ -108,7 +108,7 @@
- +