cms icon indicating copy to clipboard operation
cms copied to clipboard

[6.x] Improve delete states, and map them to variables

Open JayGeorge opened this issue 2 months ago • 1 comments

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

JayGeorge avatar Nov 27 '25 22:11 JayGeorge

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.

JayGeorge avatar Dec 01 '25 15:12 JayGeorge