diff --git a/config/cp.php b/config/cp.php
index b00d125cd6f..c18aec77aa2 100644
--- a/config/cp.php
+++ b/config/cp.php
@@ -109,8 +109,6 @@
// 'grays' => Color::Zinc,
// 'primary' => Color::Zinc[800],
- // 'success' => Color::Green[400],
- // 'danger' => Color::Red[600],
// 'ui-accent-bg' => Color::Zinc[800],
// 'ui-accent-text' => Color::Zinc[800],
@@ -134,6 +132,15 @@
// 'switch-bg' => Color::Green[500],
// 'dark-switch-bg' => Color::Green[600],
+
+ // 'success' => Color::Green[700],
+ // 'success-bg' => Color::Green[50],
+ // 'dark-success' => Color::Green[400],
+ // 'dark-success-bg' => Color::Green[950],
+ // 'danger' => Color::Red[700],
+ // 'danger-bg' => Color::Red[50],
+ // 'dark-danger' => Color::Red[400],
+ // 'dark-danger-bg' => Color::Red[950],
],
/*
diff --git a/resources/css/components/fieldtypes/table.css b/resources/css/components/fieldtypes/table.css
index d542548e249..bad59266025 100644
--- a/resources/css/components/fieldtypes/table.css
+++ b/resources/css/components/fieldtypes/table.css
@@ -1,7 +1,5 @@
-/* ==========================================================================
-Table Fieldtype
-========================================================================== */
-
+/* TABLE FIELDTYPE
+=================================================== */
.table-contained {
@apply relative mb-4 w-full rounded-lg border shadow-ui-sm outline-hidden dark:border-gray-700 text-start;
border-collapse: separate;
@@ -125,10 +123,17 @@ Table Fieldtype
.row-controls {
@apply w-8 text-center ps-0;
vertical-align: middle;
- &:focus-within {
- @apply bg-gray-100 dark:bg-gray-700;
- button {
- @apply outline-hidden ring-0;
+ }
+
+ /* TABLE FIELDTYPE / RED DELETE CONTROLS (RED)
+ =================================================== */
+ th,
+ .row-controls {
+ &:has([aria-label*="Delete"]:is(:hover, :focus)) {
+ --focus-outline-color: var(--color-danger);
+ svg {
+ color: var(--color-danger);
+ opacity: 1;
}
}
}
diff --git a/resources/css/elements/base.css b/resources/css/elements/base.css
index 86ea927392c..a1a1a55345f 100644
--- a/resources/css/elements/base.css
+++ b/resources/css/elements/base.css
@@ -10,6 +10,7 @@
--focus-within-outline-offset: 0px;
--focus-outline-color: var(--theme-color-focus-outline, var(--color-blue-400));
--focus-outline-style: solid;
+ --danger-outline-color: oklch(from var(--theme-color-danger) l c h / 0.5);
/* The outline-offset value used for buttons */
--outline-offset-button: 6px;
@@ -99,6 +100,46 @@
}
}
+/* DESTRUCTIVE STATES
+=================================================== */
+@layer utilities {
+ /* Destructive buttons */
+ [data-destructive]:not([data-ui-badge] *) {
+ --focus-outline-color: var(--danger-outline-color);
+ --focus-outline-width: 1px;
+ &:hover,
+ &:hover::before,
+ &:focus {
+ /* Delete states - use outline state instead so we don't have to worry about overriding borders, or adding borders to pseudo elements */
+ outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
+ outline-offset: var(--focus-outline-offset);
+ background: var(--color-danger-bg);
+ svg {
+ color: var(--color-danger);
+ /* Need !important to override dark mode */
+ opacity: 1!important;
+ }
+ }
+ }
+ /* DESTRUCTIVE STATES / BADGES
+ =================================================== */
+ /* When a badge "x" is hovered, the whole badge lights up red. */
+ [data-ui-badge]:has([data-destructive]:is(:hover, :focus)) {
+ --focus-outline-color: var(--danger-outline-color);
+ --focus-outline-width: 1px;
+ background: var(--color-danger-bg);
+ outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
+ outline-offset: var(--focus-outline-offset);
+ &, button {
+ color: var(--color-danger);
+ }
+ /* Cancel the outline on the actual button since we're highlighting the entire badge */
+ button {
+ outline: none;
+ }
+ }
+}
+
/* Prevent tailwind typography from adding backticks around code elements */
@utility prose {
code {
diff --git a/resources/css/ui.css b/resources/css/ui.css
index 643e6714879..c97482c5e4b 100644
--- a/resources/css/ui.css
+++ b/resources/css/ui.css
@@ -3,7 +3,7 @@
--color-primary-border: color-mix(in oklch, var(--color-primary) 100%, black 20%);
--color-primary-hover: color-mix(in oklch, var(--color-primary) 100%, black 30%);
--color-primary-gap: color-mix(in oklch, var(--color-primary) 100%, black 50%);
- --color-success: var(--theme-color-success);
+
--color-gray-50: var(--theme-color-gray-50);
--color-gray-100: var(--theme-color-gray-100);
--color-gray-150: var(--theme-color-gray-150);
@@ -17,7 +17,7 @@
--color-gray-850: var(--theme-color-gray-850);
--color-gray-900: var(--theme-color-gray-900);
--color-gray-925: var(--theme-color-gray-925);
- --color-gray-950: var(--theme-color-zinc-950);
+ --color-gray-950: var(--theme-color-gray-950);
--color-volt: oklch(93.86% 0.2018 122.24);
--color-body-bg: var(--theme-color-body-bg);
@@ -30,6 +30,11 @@
--color-ui-accent-text: var(--theme-color-ui-accent-text);
--color-switch-bg: var(--theme-color-switch-bg);
+ --color-danger-bg: var(--theme-color-danger-bg);
+ --color-success-bg: var(--theme-color-success-bg);
+ --color-success: var(--theme-color-success);
+ --color-danger: var(--theme-color-danger);
+
/* Temp */
--color-dark-100: #dfe1e5;
--color-dark-150: #bbbdc0;
@@ -83,11 +88,17 @@
--color-dark-gray: #404040;
}
-.dark {
- @theme inline {
- --color-primary-hover: color-mix(in oklch, var(--color-primary) 100%, white 30%);
- --color-success: var(--color-green-500);
- --color-gray-950: hsl(240deg 7% 8%);
+:root {
+ --animation-timing-function-fast-out-slow-in: cubic-bezier(.4,0,.2,1);
+
+ /* Custom value because gray-100 is too light and gray-200 is too dark, e.g. Bard active buttons */
+ --color-button-active: hsl(from var(--color-gray-300) h s l / 0.45);
+
+ &.dark, .dark * {
+ --color-button-active: hsl(from var(--color-gray-700) h s l / 0.45);
+ --color-danger: var(--theme-color-danger);
+ /* Make the danger background slightly darker/more subtle in dark mode */
+ --color-danger-bg: oklch(from var(--theme-color-danger-bg) l c h / 0.2);
}
}
@@ -122,16 +133,5 @@
}
}
-:root {
- --animation-timing-function-fast-out-slow-in: cubic-bezier(.4,0,.2,1);
-
- /* Custom value because gray-100 is too light and gray-200 is too dark, e.g. Bard active buttons */
- --color-button-active: hsl(from var(--color-gray-300) h s l / 0.45);
-}
-
-.dark {
- --color-button-active: hsl(from var(--color-gray-700) h s l / 0.45);
-}
-
@custom-variant with-contrast (&:where([data-contrast="increased"] *));
diff --git a/resources/js/components/assets/Editor/Editor.vue b/resources/js/components/assets/Editor/Editor.vue
index 5aa9cfd552e..7851e9daa8a 100644
--- a/resources/js/components/assets/Editor/Editor.vue
+++ b/resources/js/components/assets/Editor/Editor.vue
@@ -38,13 +38,13 @@
@completed="actionCompleted"
v-slot="{ actions }"
>
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/resources/js/components/blueprints/ImportField.vue b/resources/js/components/blueprints/ImportField.vue
index 3fa62520e5e..f8cee0ec935 100644
--- a/resources/js/components/blueprints/ImportField.vue
+++ b/resources/js/components/blueprints/ImportField.vue
@@ -15,7 +15,7 @@
-
+
-
+
diff --git a/resources/js/components/blueprints/Section.vue b/resources/js/components/blueprints/Section.vue
index 5ce43a539d7..8184254b2e0 100644
--- a/resources/js/components/blueprints/Section.vue
+++ b/resources/js/components/blueprints/Section.vue
@@ -7,8 +7,8 @@
-
-
+
+
-
+
diff --git a/resources/js/components/field-validation/Builder.vue b/resources/js/components/field-validation/Builder.vue
index 2d87c454185..a197e9e4cf5 100644
--- a/resources/js/components/field-validation/Builder.vue
+++ b/resources/js/components/field-validation/Builder.vue
@@ -76,8 +76,9 @@
diff --git a/resources/js/components/fieldtypes/ColorFieldtype.vue b/resources/js/components/fieldtypes/ColorFieldtype.vue
index c6555e04c27..4be5901a13b 100644
--- a/resources/js/components/fieldtypes/ColorFieldtype.vue
+++ b/resources/js/components/fieldtypes/ColorFieldtype.vue
@@ -69,7 +69,7 @@
/>
-
+
diff --git a/resources/js/components/fieldtypes/DictionaryFieldtype.vue b/resources/js/components/fieldtypes/DictionaryFieldtype.vue
index b6dba0dfc7c..589556e88cd 100644
--- a/resources/js/components/fieldtypes/DictionaryFieldtype.vue
+++ b/resources/js/components/fieldtypes/DictionaryFieldtype.vue
@@ -43,6 +43,7 @@
type="button"
class="-mx-3 cursor-pointer px-3 text-gray-400 hover:text-gray-700"
:aria-label="__('Deselect option')"
+ data-destructive
@click="deselect(option.value)"
>
×
diff --git a/resources/js/components/fieldtypes/FilesFieldtype.vue b/resources/js/components/fieldtypes/FilesFieldtype.vue
index b8b0c17e334..0645b5da6e6 100644
--- a/resources/js/components/fieldtypes/FilesFieldtype.vue
+++ b/resources/js/components/fieldtypes/FilesFieldtype.vue
@@ -62,6 +62,7 @@
variant="ghost"
:aria-label="__('Remove Asset')"
:title="__('Remove')"
+ destructive
/>
diff --git a/resources/js/components/fieldtypes/ListFieldtype.vue b/resources/js/components/fieldtypes/ListFieldtype.vue
index e93fedcdd85..2a6d753d6c9 100644
--- a/resources/js/components/fieldtypes/ListFieldtype.vue
+++ b/resources/js/components/fieldtypes/ListFieldtype.vue
@@ -37,7 +37,7 @@
variant="subtle"
size="xs"
round
- delete-action
+ destructive
@click="deleteValue(index)"
:aria-label="__('Delete Item')"
v-tooltip="__('Delete Item')"
diff --git a/resources/js/components/fieldtypes/TableFieldtype.vue b/resources/js/components/fieldtypes/TableFieldtype.vue
index 3e32d12f6f2..83ddd702fe9 100644
--- a/resources/js/components/fieldtypes/TableFieldtype.vue
+++ b/resources/js/components/fieldtypes/TableFieldtype.vue
@@ -20,7 +20,7 @@
{{ index + 1 }}
-
+
|
|
@@ -46,7 +46,7 @@
/>
-
+
|
diff --git a/resources/js/components/fieldtypes/assets/AssetRow.vue b/resources/js/components/fieldtypes/assets/AssetRow.vue
index 301fbc0416b..2152d12925f 100644
--- a/resources/js/components/fieldtypes/assets/AssetRow.vue
+++ b/resources/js/components/fieldtypes/assets/AssetRow.vue
@@ -49,6 +49,7 @@
variant="ghost"
:aria-label="__('Remove Asset')"
:title="__('Remove')"
+ destructive
/>
-
+
diff --git a/resources/js/components/fieldtypes/bard/Image.vue b/resources/js/components/fieldtypes/bard/Image.vue
index 185e0c96499..dd8005c7665 100644
--- a/resources/js/components/fieldtypes/bard/Image.vue
+++ b/resources/js/components/fieldtypes/bard/Image.vue
@@ -19,7 +19,7 @@
-
+
-
+
@@ -38,6 +38,7 @@
:body-text="__('Are you sure? Unsaved changes will be lost.')"
:button-text="__('Discard Changes')"
:danger="true"
+ :aria-label="__('Discard Changes')"
@confirm="confirmCloseWithChanges"
@cancel="closingWithChanges = false"
/>
diff --git a/resources/js/components/inputs/relationship/Item.vue b/resources/js/components/inputs/relationship/Item.vue
index ee3fa386f19..9d29c4d2177 100644
--- a/resources/js/components/inputs/relationship/Item.vue
+++ b/resources/js/components/inputs/relationship/Item.vue
@@ -52,11 +52,13 @@
v-if="editable"
:text="__('Edit')"
@click="edit"
+ :aria-label="__('Edit')"
/>
diff --git a/resources/js/components/ui/Button/Button.vue b/resources/js/components/ui/Button/Button.vue
index cff09ac937a..432252afe79 100644
--- a/resources/js/components/ui/Button/Button.vue
+++ b/resources/js/components/ui/Button/Button.vue
@@ -19,6 +19,7 @@ const props = defineProps({
text: { type: [String, Number, Boolean, null], default: null },
type: { type: String, default: 'button' },
variant: { type: String, default: 'default' },
+ destructive: { type: Boolean, default: false },
});
const slots = useSlots();
@@ -48,7 +49,7 @@ const buttonClasses = computed(() => {
filled: 'bg-black/5 hover:bg-black/10 hover:text-gray-900 dark:hover:text-white dark:bg-white/15 dark:hover:bg-white/20 [&_svg]:opacity-70',
ghost: 'bg-transparent hover:bg-gray-400/10 text-gray-900 dark:text-gray-300 dark:hover:bg-white/7 dark:hover:text-gray-200',
'ghost-pressed': 'bg-transparent hover:bg-gray-400/10 text-black dark:text-white dark:hover:bg-white/7 dark:hover:text-white [&_svg]:opacity-100',
- subtle: 'bg-transparent hover:bg-gray-400/10 text-gray-500 hover:text-gray-700 dark:text-gray-300 dark:hover:bg-white/7 dark:hover:text-gray-200 [&_svg]:opacity-35',
+ subtle: 'bg-transparent hover:bg-gray-400/10 text-gray-500 hover:text-gray-700 dark:text-gray-300 dark:hover:bg-white/7 dark:hover:text-gray-200',
pressed: [
'bg-linear-to-b from-gray-200 to-gray-150 text-gray-900 border border-gray-300 inset-shadow-sm/10',
'dark:from-black dark:to-black dark:text-white dark:border-gray-700/80',
@@ -63,8 +64,8 @@ const buttonClasses = computed(() => {
},
groupBorder: {
danger: [
- 'in-data-ui-button-group:text-red-500 in-data-ui-button-group:bg-linear-to-b in-data-ui-button-group:from-white in-data-ui-button-group:to-red-50 in-data-ui-button-group:hover:to-gray-100 in-data-ui-button-group:hover:bg-gray-50 in-data-ui-button-group:border in-data-ui-button-group:border-gray-300 in-data-ui-button-group:shadow-ui-sm in-data-ui-button-group:inset-shadow-none',
- 'dark:in-data-ui-button-group:text-red-500 dark:in-data-ui-button-group:from-gray-850 dark:in-data-ui-button-group:to-red-900/10 dark:in-data-ui-button-group:hover:to-gray-850 dark:in-data-ui-button-group:hover:bg-gray-900 dark:in-data-ui-button-group:border-gray-700/80 dark:in-data-ui-button-group:shadow-ui-md',
+ 'in-data-ui-button-group:text-danger in-data-ui-button-group:bg-linear-to-b in-data-ui-button-group:from-white in-data-ui-button-group:to-red-50 in-data-ui-button-group:hover:to-gray-100 in-data-ui-button-group:hover:bg-gray-50 in-data-ui-button-group:border in-data-ui-button-group:border-gray-300 in-data-ui-button-group:shadow-ui-sm in-data-ui-button-group:inset-shadow-none',
+ 'dark:in-data-ui-button-group:text-danger dark:in-data-ui-button-group:from-gray-850 dark:in-data-ui-button-group:to-red-900/10 dark:in-data-ui-button-group:hover:to-gray-850 dark:in-data-ui-button-group:hover:bg-gray-900 dark:in-data-ui-button-group:border-gray-700/80 dark:in-data-ui-button-group:shadow-ui-md',
],
ghost: '',
pressed: 'in-data-ui-button-group:border-s-0 [:is([data-ui-button-group]>&:first-child,_[data-ui-button-group]_:first-child>&)]:border-s-[1px]',
@@ -105,6 +106,7 @@ const buttonClasses = computed(() => {
:class="buttonClasses"
:disabled="loading"
:data-ui-group-target="['subtle', 'ghost'].includes(props.variant) ? null : true"
+ :data-destructive="destructive || undefined"
:href
:target
:type="props.href ? null : type"
diff --git a/resources/js/components/ui/Combobox/Combobox.vue b/resources/js/components/ui/Combobox/Combobox.vue
index 1cebaea6264..a2c848f8089 100644
--- a/resources/js/components/ui/Combobox/Combobox.vue
+++ b/resources/js/components/ui/Combobox/Combobox.vue
@@ -398,8 +398,8 @@ defineExpose({
-
-
+
+
diff --git a/resources/js/components/ui/Context/Item.vue b/resources/js/components/ui/Context/Item.vue
index 66ba698e92d..90a167e9e4f 100644
--- a/resources/js/components/ui/Context/Item.vue
+++ b/resources/js/components/ui/Context/Item.vue
@@ -26,7 +26,7 @@ const classes = cva({
variants: {
variant: {
default: 'text-gray-700 dark:text-gray-300',
- destructive: 'text-red-600',
+ destructive: 'text-danger! hover:not-data-disabled:bg-danger-bg!',
},
},
})({ variant: props.variant });
@@ -36,7 +36,7 @@ const iconClasses = cva({
base: 'size-3.5!',
variant: {
default: 'text-gray-500',
- destructive: 'text-red-500!',
+ destructive: 'text-danger!',
},
},
})({ variant: props.variant });
diff --git a/resources/js/components/ui/DatePicker/DatePicker.vue b/resources/js/components/ui/DatePicker/DatePicker.vue
index f6f9d5ebdaf..a052bf84fa9 100644
--- a/resources/js/components/ui/DatePicker/DatePicker.vue
+++ b/resources/js/components/ui/DatePicker/DatePicker.vue
@@ -187,7 +187,9 @@ const getInputLabel = (part) => {
icon="x"
class="-my-1.25 -me-2"
:disabled="disabled"
+ :aria-label="__('Clear date')"
v-tooltip="__('Clear date')"
+ destructive
/>
diff --git a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue
index fd22b8a47b0..3c6e90b167b 100644
--- a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue
+++ b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue
@@ -148,7 +148,18 @@ const calendarEvents = computed(() => ({
-
+
diff --git a/resources/js/components/ui/Dropdown/Item.vue b/resources/js/components/ui/Dropdown/Item.vue
index 9d561dfb4d6..78c7b5e474b 100644
--- a/resources/js/components/ui/Dropdown/Item.vue
+++ b/resources/js/components/ui/Dropdown/Item.vue
@@ -27,7 +27,7 @@ const classes = cva({
variants: {
variant: {
default: 'text-gray-700 dark:text-gray-300',
- destructive: 'text-red-600 dark:text-red-500',
+ destructive: 'text-danger! hover:not-data-disabled:bg-danger-bg!',
},
},
})({ variant: props.variant });
@@ -37,7 +37,7 @@ const iconClasses = cva({
base: 'size-3.5!',
variant: {
default: 'text-gray-500',
- destructive: 'text-red-500!',
+ destructive: 'text-danger!',
},
},
})({ variant: props.variant });
diff --git a/resources/js/components/ui/Label.vue b/resources/js/components/ui/Label.vue
index 597a6356fd0..d1a25c6c841 100644
--- a/resources/js/components/ui/Label.vue
+++ b/resources/js/components/ui/Label.vue
@@ -22,7 +22,7 @@ const props = defineProps({
{{ text }}
- *
+ *
diff --git a/resources/js/components/ui/Listing/FieldFilterRow.vue b/resources/js/components/ui/Listing/FieldFilterRow.vue
index 6eff0517409..3429ccc2f67 100644
--- a/resources/js/components/ui/Listing/FieldFilterRow.vue
+++ b/resources/js/components/ui/Listing/FieldFilterRow.vue
@@ -66,7 +66,7 @@ defineExpose({
/>
-
+
diff --git a/resources/js/components/ui/Listing/Filters.vue b/resources/js/components/ui/Listing/Filters.vue
index ada70bda772..4b0f1b45f5d 100644
--- a/resources/js/components/ui/Listing/Filters.vue
+++ b/resources/js/components/ui/Listing/Filters.vue
@@ -121,6 +121,7 @@ function handleStackClosed() {
size="sm"
class="absolute! top-1.75 right-3 z-(--z-index-above) [&_svg]:size-4"
@click="handleStackClosed"
+ aria-label="Close filters"
/>
@@ -166,7 +167,7 @@ function handleStackClosed() {
:icon-append="reorderable ? null : 'x'"
:text="badge"
:disabled="reorderable"
- class="last:me-12"
+ class="last:me-12 hover:bg-danger-bg! hover:text-danger! hover:[&_svg]:text-danger hover:outline hover:outline-(--danger-outline-color)! hover:-outline-offset-1"
@click="removeFieldFilter(handle)"
/>
diff --git a/resources/js/components/ui/TimePicker/TimePicker.vue b/resources/js/components/ui/TimePicker/TimePicker.vue
index ad773cab7ba..5dd2412eafa 100644
--- a/resources/js/components/ui/TimePicker/TimePicker.vue
+++ b/resources/js/components/ui/TimePicker/TimePicker.vue
@@ -51,8 +51,8 @@ const setToNow = () => {
-
-
+
+
diff --git a/resources/views/partials/head.blade.php b/resources/views/partials/head.blade.php
index 28c015f246d..be352bbf207 100644
--- a/resources/views/partials/head.blade.php
+++ b/resources/views/partials/head.blade.php
@@ -50,7 +50,7 @@
:root {
{{ \Statamic\CP\Color::cssVariables() }}
- &.dark {
+ &.dark, .dark {
{{ \Statamic\CP\Color::cssVariables(dark: true) }}
}
}
diff --git a/src/CP/Color.php b/src/CP/Color.php
index 4758d73624e..fbf61b9856d 100644
--- a/src/CP/Color.php
+++ b/src/CP/Color.php
@@ -401,8 +401,7 @@ public static function defaults(bool $dark = false): array
'gray-900' => self::Zinc[900],
'gray-925' => self::Zinc[925],
'gray-950' => self::Zinc[950],
- 'success' => self::Green[400],
- 'danger' => self::Red[600],
+ 'dark-gray-950' => 'hsl(240deg 7% 8%)',
'body-bg' => self::Zinc[100],
'body-border' => self::Transparent,
'dark-body-bg' => self::Zinc[900],
@@ -418,6 +417,14 @@ public static function defaults(bool $dark = false): array
'ui-accent-text' => 'var(--theme-color-ui-accent-bg)',
'dark-ui-accent-text' => self::Indigo[400],
'switch-bg' => 'var(--theme-color-ui-accent-bg)',
+ 'danger' => self::Red[700],
+ 'danger-bg' => self::Red[50],
+ 'dark-danger' => self::Red[400],
+ 'dark-danger-bg' => self::Red[950],
+ 'success' => self::Green[700],
+ 'success-bg' => self::Green[50],
+ 'dark-success' => self::Green[400],
+ 'dark-success-bg' => self::Green[950],
])
->filter(fn ($color, $name) => str($name)->startsWith('dark-') ? $dark : ! $dark)
->all();