Skip to content

Conversation

@JayGeorge
Copy link

@JayGeorge JayGeorge commented Nov 27, 2025

This PR adds delete/danger and success states and maps them to variables such as red and green.

My goal was to style these states in a way that they're obvious but not garish.

These states are also now mapped to variables and kick in automatically when aria keywords are used on buttons such as "Delete", "Clear", "Remove", and "Reset". (As part of this I've sprinkled a few aria-roles where we have just an icon by itself)

Here are some screenshots of the delete styles:

Deleting tags is now very obvious

2025-11-27 at 21 58 08@2x

Column and row deletion is highlighted

2025-11-27 at 21 59 18@2x

Example of a clear selection button hover/focus state

2025-11-27 at 22 00 34@2x

Example of dark mode adjustments

2025-11-27 at 22 02 38@2x

…metimes the backgrounds stack up and you get undesirable darker colors, and fix .dark overrides for @theme inline vars, based on using a dark mode background for delete buttons
…bout overriding borders, or adding borders to pseudo elements
I think the convention is warning = yellow, red = danger
…o "danger" My suspicion is that we now need !important because TW compiles classes alphabetically and "danger" will come earlier in the cascade vs "warning"
Copy link
Member

@jasonvarga jasonvarga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the aria label as a selector will break if you're using the CP in a language other than English. e.g. When using French you'd have html like aria-label="Supprimer" instead of aria-label="Delete".

…nts working when not descended from dark mode (the root) For example, I was having trouble switching `/cp/assets > edit > delete button` to switch to the dark variant
…hen targeting things descended from .dark panels, for example `/cp/assets > edit > delete button` , which should be a dark variant
@JayGeorge
Copy link
Author

JayGeorge commented Dec 1, 2025

Using the aria label as a selector will break if you're using the CP in a language other than English

Gah, of course! My bad, I shouldn't have relied on them.


As discussed in Slack—these modifiers now rely on a prop, which just changes the hover/focus state. This way a subtle variant will still stay look like a subtle variant until the user interacts with it.


I tried turning this into Tailwind, but honestly, I find it incredibly difficult to parse:

This is the native CSS, which is reasonable to follow:

/* 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; } } }

Vs the Tailwind implementation, which is very verbose:

{ destructive: true, class: `  [border border-red-500--focus-outline-color:var(--danger-outline-color)]  [--focus-outline-width:1px]   hover:outline  hover:outline-[var(--focus-outline-width)]  hover:outline-[var(--focus-outline-style)]  hover:outline-[var(--focus-outline-color)]  hover:[outline-offset:var(--focus-outline-offset)]  hover:bg-[var(--color-danger-bg)]  hover:[&_svg]:text-[var(--color-danger)]  hover:[&_svg]:!opacity-100   focus:outline  focus:outline-[var(--focus-outline-width)]  focus:outline-[var(--focus-outline-style)]  focus:outline-[var(--focus-outline-color)]  focus:[outline-offset:var(--focus-outline-offset)]  focus:bg-[var(--color-danger-bg)]  focus:[&_svg]:text-[var(--color-danger)]  focus:[&_svg]:!opacity-100   focus-visible:outline  focus-visible:outline-[var(--focus-outline-width)]  focus-visible:outline-[var(--focus-outline-style)]  focus-visible:outline-[var(--focus-outline-color)]  focus-visible:[outline-offset:var(--focus-outline-offset)]  focus-visible:bg-[var(--color-danger-bg)]  focus-visible:[&_svg]:text-[var(--color-danger)]  focus-visible:[&_svg]:!opacity-100  ` },

We're also targeting the badge container with :has for badges, which would add a whole new level of complexity to the Tailwind implementation, vs this, which I think is easy enough to follow:

/* 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; } }

I'm all in on defaulting to Tailwind 🤗 , but I think this is an occasion where it's much easier to parse a few nested brackets of native CSS.

accepting master Conflicts in:	resources/js/components/blueprints/ImportField.vue	resources/js/components/fieldtypes/ListFieldtype.vue
@JayGeorge JayGeorge requested a review from jasonvarga December 2, 2025 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants