max / makenotwork
13 files changed,
+101 insertions,
-56 deletions
| @@ -92,7 +92,7 @@ | |||
| 92 | 92 | | Section lead | `.section-lead` | with `.mb-section` / `.text-sm` / `.dimmed` utilities | — | | |
| 93 | 93 | | Section divider | `.section-divider` | — | — | | |
| 94 | 94 | | Section grouping label | `.section-group-label` | — | — | | |
| 95 | - | | Badge | `.badge` | `.badge-success`, `.free`, plus state classes `.pending` / `.draft` / `.failed` | `.is-selected`, `.is-faded`, `.active` (= completion, not selection) | | |
| 95 | + | | Badge | `.badge` | status set `.badge--live` / `.badge--pending` / `.badge--failed`; `.free` (price fact); `.ai-tier-*` (disclosure level) | `.is-faded` | | |
| 96 | 96 | | Tag | `.tag` (inside `.tag-input` for editing) | — | — | | |
| 97 | 97 | | Callout (solid-tint inline) | `.callout` | `--danger`, `--warning`, `--solid-warning` | — | | |
| 98 | 98 | | Alert (left-border inline) | `.alert` | `-note`, `-tip`, `-important`, `-warning`, `-caution` | — | | |
| @@ -150,11 +150,12 @@ | |||
| 150 | 150 | Exactly one spelling for each interaction state, applied to every interactive primitive: | |
| 151 | 151 | ||
| 152 | 152 | - **Hover**: fill shift to `var(--hover-surface)`, on every interactive primitive. One spelling, no component-type branch. The exception is `.btn--link`, which is semantically a link and has no surface to shift: it fades to opacity `0.6`. | |
| 153 | - | - **Press**: `:active` inverts the bevel, `box-shadow: var(--shadow-inset)`. Nothing moves; there is no `transform: translate(2px, 2px)` and no `box-shadow: none`. | |
| 153 | + | - **Press**: `:active` inverts the bevel, `box-shadow: var(--shadow-inset)`. Nothing moves; there is no `transform: translate(2px, 2px)`, and no control opts out. A flat control inverts on press the same as a raised one, including `.btn--link` and the menu and disclosure openers. | |
| 154 | 154 | ||
| 155 | 155 | Hover used to have three options picked by component type, and the second of them was a depth lift: the shadow grew from `var(--shadow-raised)` to `3px 3px var(--shadow-edge)`, reserved for controls that commit a write, so that pressing "Pay" looked different from pressing "Next". That signal is gone rather than moved, and it was spent deliberately. A bevel is an edge and not a distance, so it has no growth axis to extend along; keeping a commit-versus-navigate cue would have meant a local override on top of the `layout.css` every other make-family app takes unmodified. Decided 2026-07-31 (wiki `mnw-platinum`). A control that commits a write is distinguished by its label and its placement, the same way it is in GoingsOn and Balanced Breakfast. | |
| 156 | - | - **Focus**: `:focus-visible` shows the `--focus-ring` violet outline. Custom interactive containers (`.type-card`, `.pricing-card`, sort headers) must opt in by adding `:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }`. | |
| 157 | - | - **Selected / active**: `.is-selected` modifier applies `background: var(--highlight-faint)` plus the focus-ring border. Migrate `.tab.active`, `.filter-item.active`, `.view-btn.active`, `.badge.active`, and the `:checked + .type-card-inner` recipe onto this single class. | |
| 156 | + | - **Focus**: `:focus-visible` shows the `--focus-ring` violet outline. Custom interactive containers (`.card--selectable`, sort headers) must opt in by adding `:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }`. A container the browser does not focus on its own also needs `tabindex="0"` in the markup, or the rule never matches and the outline is decoration for a state that cannot happen. The `data-action` dispatcher activates such a container on Enter and Space. | |
| 157 | + | - **Selected / active**: `.is-selected` modifier applies `background: var(--highlight-faint)` plus the focus-ring border. `.tab.active`, `.filter-item.active`, `.view-btn.active`, and the `:checked + .card--selectable-inner` recipe all read it. | |
| 158 | + | - **Status**: a badge names the status it reports, and the colour follows from the status. The set is `.badge--live` (Enabled, Active, Available, Connected, Current), `.badge--pending` (Pending, Draft) and `.badge--failed` (Failed, Suspended). A badge reports data, not selection, so it never takes `.is-selected` and there is no `.badge.active`. A class that names a colour rather than a state is the same category error as `.mb-4` naming a number. `.free` and the `.ai-tier-*` trio stay outside the set: a price fact and a disclosure level are not lifecycle. | |
| 158 | 159 | - **Disabled**: `:disabled` and `[aria-disabled="true"]` show `opacity: 0.5` and `cursor: not-allowed`. | |
| 159 | 160 | - **Busy / loading**: HTMX-driven via `.htmx-request` on the trigger. There is no skeleton primitive: one was documented here for months and never built, so it was removed rather than left as a promise. | |
| 160 | 161 |
| @@ -187,8 +187,8 @@ | |||
| 187 | 187 | APP-LOCAL CONSTANTS, not theme colors, not derivable from intents. | |
| 188 | 188 | ||
| 189 | 189 | Component CSS references the intent tokens above directly (no brand | |
| 190 | - | aliases). Status backgrounds and the faint accent tint derive from the | |
| 191 | - | intents inline via color-mix(); these few stay fixed: | |
| 190 | + | aliases). Status backgrounds derive from the intents inline via | |
| 191 | + | color-mix(); these few stay fixed: | |
| 192 | 192 | - pure black/white contrast anchors | |
| 193 | 193 | - Stripe's brand color (an external constant) | |
| 194 | 194 | - diff add/del, a hard readability convention that must not re-theme | |
| @@ -197,6 +197,11 @@ | |||
| 197 | 197 | --primary-light: #ffffff; | |
| 198 | 198 | --stripe: #635BFF; | |
| 199 | 199 | ||
| 200 | + | /* The faint accent tint every selection and highlight recipe fills with. | |
| 201 | + | Derived from --action, so it re-tints with the creator's theme. Named | |
| 202 | + | because the charter names it and because a dozen rules read it. */ | |
| 203 | + | --highlight-faint: color-mix(in oklch, var(--action) 8%, transparent); | |
| 204 | + | ||
| 200 | 205 | /* Diff / code review, fixed green-add / red-del convention */ | |
| 201 | 206 | --diff-add: #2d7d2d; | |
| 202 | 207 | --diff-add-bg: rgba(45, 125, 45, 0.08); | |
| @@ -558,14 +563,12 @@ | |||
| 558 | 563 | } | |
| 559 | 564 | ||
| 560 | 565 | .btn--link:active { | |
| 561 | - | box-shadow: none; | |
| 562 | - | transform: none; | |
| 566 | + | box-shadow: var(--shadow-inset); | |
| 563 | 567 | } | |
| 564 | 568 | ||
| 565 | 569 | /* Menu / disclosure openers, buttons whose job is to reveal a menu, | |
| 566 | - | filter panel, expander, or popover. They navigate UI, they don't | |
| 567 | - | commit anything, so they override the base `button` depth-lift back | |
| 568 | - | to flat. Add new opener selectors here as they appear. */ | |
| 570 | + | filter panel, expander, or popover. They sit flat rather than raised. | |
| 571 | + | Add new opener selectors here as they appear. */ | |
| 569 | 572 | .context-menu-btn, | |
| 570 | 573 | .discover-filter-toggle, | |
| 571 | 574 | .tip-toggle, | |
| @@ -584,8 +587,7 @@ | |||
| 584 | 587 | .discover-filter-toggle:active, | |
| 585 | 588 | .tip-toggle:active, | |
| 586 | 589 | .bundle-toggle:active { | |
| 587 | - | box-shadow: none; | |
| 588 | - | transform: none; | |
| 590 | + | box-shadow: var(--shadow-inset); | |
| 589 | 591 | } | |
| 590 | 592 | ||
| 591 | 593 | /* =========================================== | |
| @@ -857,6 +859,11 @@ | |||
| 857 | 859 | background: var(--border); | |
| 858 | 860 | } | |
| 859 | 861 | ||
| 862 | + | .data-table th.sortable:focus-visible { | |
| 863 | + | outline: 2px solid var(--focus-ring); | |
| 864 | + | outline-offset: 2px; | |
| 865 | + | } | |
| 866 | + | ||
| 860 | 867 | .data-table th.sortable::after { | |
| 861 | 868 | content: " ^"; | |
| 862 | 869 | opacity: 0.3; | |
| @@ -998,32 +1005,39 @@ | |||
| 998 | 1005 | color: var(--primary-light); | |
| 999 | 1006 | } | |
| 1000 | 1007 | ||
| 1001 | - | .badge-success, | |
| 1002 | - | .badge.active, | |
| 1003 | - | .badge.complete { | |
| 1008 | + | /* Status vocabulary (charter: docs/design-system.md). A badge names the status | |
| 1009 | + | it reports, and the colour follows from the status. */ | |
| 1010 | + | .badge--live { | |
| 1004 | 1011 | background: var(--success); | |
| 1005 | 1012 | } | |
| 1006 | 1013 | ||
| 1007 | - | .badge.pending, | |
| 1008 | - | .badge.draft { | |
| 1014 | + | .badge--pending { | |
| 1009 | 1015 | background: var(--warning); | |
| 1010 | 1016 | } | |
| 1011 | 1017 | ||
| 1012 | - | .badge.failed { | |
| 1018 | + | .badge--failed { | |
| 1013 | 1019 | background: var(--danger); | |
| 1014 | 1020 | } | |
| 1015 | 1021 | ||
| 1022 | + | /* Raw-status classes. Several templates interpolate a status column straight | |
| 1023 | + | into the class attribute (`class="badge {{ tx.status|lowercase }}"`), so these | |
| 1024 | + | names are written by the data rather than chosen by an author. GoingsOn task | |
| 1025 | + | 8a007e42 moves that behind a status_class() helper on the view model, which | |
| 1026 | + | returns a name from the set above; this block goes when that lands. */ | |
| 1027 | + | .badge.active, | |
| 1016 | 1028 | .badge.current { | |
| 1017 | 1029 | background: var(--success); | |
| 1018 | 1030 | } | |
| 1019 | 1031 | ||
| 1032 | + | .badge.pending, | |
| 1033 | + | .badge.draft, | |
| 1020 | 1034 | .badge.suppressed { | |
| 1021 | 1035 | background: var(--warning); | |
| 1022 | 1036 | } | |
| 1023 | 1037 | ||
| 1038 | + | .badge.failed, | |
| 1024 | 1039 | .badge.suspended { | |
| 1025 | - | background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); | |
| 1026 | - | color: var(--danger); | |
| 1040 | + | background: var(--danger); | |
| 1027 | 1041 | } | |
| 1028 | 1042 | ||
| 1029 | 1043 | .badge.free { | |
| @@ -4315,7 +4329,7 @@ | |||
| 4315 | 4329 | } | |
| 4316 | 4330 | ||
| 4317 | 4331 | .banner--info { | |
| 4318 | - | background: color-mix(in oklch, var(--action) 8%, transparent); | |
| 4332 | + | background: var(--highlight-faint); | |
| 4319 | 4333 | border-bottom: 2px solid var(--focus-ring); | |
| 4320 | 4334 | } | |
| 4321 | 4335 | ||
| @@ -5091,12 +5105,13 @@ | |||
| 5091 | 5105 | ||
| 5092 | 5106 | /* Canonical "selected" recipe (charter: docs/design-system.md). | |
| 5093 | 5107 | Apply `.is-selected` to any interactive container to mark it as the | |
| 5094 | - | currently-selected option. `.badge.active` is intentionally distinct. | |
| 5095 | - | It expresses completion status, not selection. Visibility toggles | |
| 5096 | - | (`.tab-content.active`, `.section-panel.active`, `.editor-panel.active`) | |
| 5097 | - | also keep `.active` since they're not selection state. */ | |
| 5108 | + | currently-selected option. A badge never takes it: a badge reports a status | |
| 5109 | + | and nobody selects "Enabled", so badges use the `.badge--*` status set. | |
| 5110 | + | Visibility toggles (`.tab-content.active`, `.section-panel.active`, | |
| 5111 | + | `.editor-panel.active`) also keep `.active` since they're not selection | |
| 5112 | + | state. */ | |
| 5098 | 5113 | .is-selected { | |
| 5099 | - | background: color-mix(in oklch, var(--action) 8%, transparent); | |
| 5114 | + | background: var(--highlight-faint); | |
| 5100 | 5115 | border-color: var(--focus-ring); | |
| 5101 | 5116 | } | |
| 5102 | 5117 | ||
| @@ -5266,8 +5281,7 @@ | |||
| 5266 | 5281 | } | |
| 5267 | 5282 | ||
| 5268 | 5283 | .order-btn:active { | |
| 5269 | - | background: var(--primary-dark); | |
| 5270 | - | color: var(--primary-light); | |
| 5284 | + | box-shadow: var(--shadow-inset); | |
| 5271 | 5285 | } | |
| 5272 | 5286 | ||
| 5273 | 5287 | /* =========================================== | |
| @@ -5870,7 +5884,7 @@ | |||
| 5870 | 5884 | ||
| 5871 | 5885 | .fork-card--founder { | |
| 5872 | 5886 | border-color: var(--action); | |
| 5873 | - | box-shadow: 0 0 0 1px color-mix(in oklch, var(--action) 8%, transparent); | |
| 5887 | + | box-shadow: 0 0 0 1px var(--highlight-faint); | |
| 5874 | 5888 | } | |
| 5875 | 5889 | ||
| 5876 | 5890 | .founder-tier-grid { | |
| @@ -7066,7 +7080,7 @@ | |||
| 7066 | 7080 | } | |
| 7067 | 7081 | .git-diff-hunk-header td { | |
| 7068 | 7082 | padding: var(--gap-bound) var(--gap-peer); | |
| 7069 | - | background: color-mix(in oklch, var(--action) 8%, transparent); | |
| 7083 | + | background: var(--highlight-faint); | |
| 7070 | 7084 | color: var(--content); | |
| 7071 | 7085 | opacity: 0.6; | |
| 7072 | 7086 | font-size: var(--text-fine); | |
| @@ -7106,7 +7120,7 @@ | |||
| 7106 | 7120 | } | |
| 7107 | 7121 | .git-file-content .line-number a:hover { opacity: 0.7; } | |
| 7108 | 7122 | .git-file-content tr:target td, | |
| 7109 | - | .git-file-content tr.highlighted td { background: color-mix(in oklch, var(--action) 8%, transparent); } | |
| 7123 | + | .git-file-content tr.highlighted td { background: var(--highlight-faint); } | |
| 7110 | 7124 | ||
| 7111 | 7125 | /* Git blame view */ | |
| 7112 | 7126 | .git-blame-content { overflow-x: auto; border: 1px solid var(--border); } | |
| @@ -7453,7 +7467,7 @@ | |||
| 7453 | 7467 | .filter-item .count { opacity: 0.4; font-size: var(--text-fine); } | |
| 7454 | 7468 | .tag-follow-btn { background: none; border: 1px solid var(--border); cursor: pointer; font-family: var(--font-mono); font-size: var(--text-fine); opacity: 0.55; padding: var(--gap-bound) var(--gap-peer); color: var(--content); } | |
| 7455 | 7469 | .tag-follow-btn:hover { opacity: 1; } | |
| 7456 | - | .tag-follow-btn.is-selected { opacity: 0.85; border-color: var(--focus-ring); background: color-mix(in oklch, var(--action) 8%, transparent); } | |
| 7470 | + | .tag-follow-btn.is-selected { opacity: 0.85; border-color: var(--focus-ring); background: var(--highlight-faint); } | |
| 7457 | 7471 | .filter-checkbox { display: flex; align-items: center; gap: var(--gap-peer); padding: var(--gap-bound) 0; cursor: pointer; } | |
| 7458 | 7472 | .filter-checkbox input { width: auto; margin: 0; } | |
| 7459 | 7473 | .price-inputs { display: flex; gap: var(--gap-bound); align-items: center; margin-bottom: var(--gap-peer); } | |
| @@ -7472,7 +7486,7 @@ | |||
| 7472 | 7486 | /* Selected tags. Also the active-filter summary, so they use the canonical | |
| 7473 | 7487 | selected tint rather than a new colour. */ | |
| 7474 | 7488 | .tag-chips { list-style: none; display: flex; flex-wrap: wrap; gap: var(--gap-bound); margin-bottom: var(--gap-section); } | |
| 7475 | - | .tag-chip { display: flex; align-items: center; gap: var(--gap-bound); padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine); background: color-mix(in oklch, var(--action) 8%, transparent); border: 1px solid var(--focus-ring); } | |
| 7489 | + | .tag-chip { display: flex; align-items: center; gap: var(--gap-bound); padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine); background: var(--highlight-faint); border: 1px solid var(--focus-ring); } | |
| 7476 | 7490 | .tag-chip-label { overflow-wrap: anywhere; } | |
| 7477 | 7491 | .tag-chip-remove { text-decoration: none; color: var(--content-secondary); font-size: var(--text-body); line-height: 1; padding: 0 var(--gap-bound); } | |
| 7478 | 7492 | .tag-chip-remove:hover { color: var(--content); } | |
| @@ -7573,7 +7587,7 @@ | |||
| 7573 | 7587 | .issue-tab:hover { opacity: 1; } | |
| 7574 | 7588 | .issue-tab.is-selected { | |
| 7575 | 7589 | opacity: 1; | |
| 7576 | - | background: color-mix(in oklch, var(--action) 8%, transparent); | |
| 7590 | + | background: var(--highlight-faint); | |
| 7577 | 7591 | border-color: var(--focus-ring); | |
| 7578 | 7592 | } | |
| 7579 | 7593 | .page-info { opacity: 0.6; } | |
| @@ -7680,13 +7694,22 @@ | |||
| 7680 | 7694 | .card--selectable input:checked + .card--selectable-inner, | |
| 7681 | 7695 | .card--selectable.is-selected .card--selectable-inner { | |
| 7682 | 7696 | border-color: var(--action); | |
| 7683 | - | background: color-mix(in oklch, var(--action) 8%, transparent); | |
| 7697 | + | background: var(--highlight-faint); | |
| 7684 | 7698 | } | |
| 7685 | 7699 | ||
| 7686 | 7700 | .card--selectable:hover .card--selectable-inner { | |
| 7687 | 7701 | background: var(--surface-sunken); | |
| 7688 | 7702 | } | |
| 7689 | 7703 | ||
| 7704 | + | /* Focus. The card is a <label> wrapping a visually hidden radio, so the focus | |
| 7705 | + | lands on the input and the ring has to be drawn on the inner surface. The | |
| 7706 | + | <a> form of the card takes focus itself. */ | |
| 7707 | + | .card--selectable:focus-visible, | |
| 7708 | + | .card--selectable input:focus-visible + .card--selectable-inner { | |
| 7709 | + | outline: 2px solid var(--focus-ring); | |
| 7710 | + | outline-offset: 2px; | |
| 7711 | + | } | |
| 7712 | + | ||
| 7690 | 7713 | .card--selectable-label { | |
| 7691 | 7714 | font-weight: bold; | |
| 7692 | 7715 | font-size: var(--text-note); | |
| @@ -8336,6 +8359,7 @@ | |||
| 8336 | 8359 | ||
| 8337 | 8360 | /* Sortable table column header */ | |
| 8338 | 8361 | .sortable-th { cursor: pointer; } | |
| 8362 | + | .sortable-th:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } | |
| 8339 | 8363 | .sort-arrow { opacity: 0.4; } | |
| 8340 | 8364 | .sort-arrow--active { opacity: 1; } | |
| 8341 | 8365 |
| @@ -24,9 +24,9 @@ | |||
| 24 | 24 | <h1 class="page-title">{{ item.title }}</h1> | |
| 25 | 25 | <div class="item-meta"> | |
| 26 | 26 | {% if item.is_public && item.publish_at.is_none() %} | |
| 27 | - | <span class="badge active">Active</span> | |
| 27 | + | <span class="badge badge--live">Active</span> | |
| 28 | 28 | {% else if !item.is_public && item.publish_at.is_some() %} | |
| 29 | - | <span class="badge pending">Scheduled</span> | |
| 29 | + | <span class="badge badge--pending">Scheduled</span> | |
| 30 | 30 | {% else %} | |
| 31 | 31 | <span class="badge inactive">Draft</span> | |
| 32 | 32 | {% endif %} |
| @@ -28,7 +28,7 @@ | |||
| 28 | 28 | <td class="text-sm nowrap">{{ user.created_at }}</td> | |
| 29 | 29 | <td> | |
| 30 | 30 | {% if user.is_suspended %} | |
| 31 | - | <span class="badge suspended">Suspended</span> | |
| 31 | + | <span class="badge badge--failed">Suspended</span> | |
| 32 | 32 | {% if user.has_pending_appeal %} | |
| 33 | 33 | <span class="text-xs dimmed">(appeal pending)</span> | |
| 34 | 34 | {% endif %} | |
| @@ -52,7 +52,7 @@ | |||
| 52 | 52 | </td> | |
| 53 | 53 | <td class="nowrap"> | |
| 54 | 54 | {% if user.custom_pages_locked %} | |
| 55 | - | <span class="badge suspended">Locked</span> | |
| 55 | + | <span class="badge badge--failed">Locked</span> | |
| 56 | 56 | <button class="btn-secondary small" | |
| 57 | 57 | hx-post="/api/admin/users/{{ user.id }}/unlock-pages" | |
| 58 | 58 | hx-target="#users-table" |
| @@ -28,7 +28,7 @@ | |||
| 28 | 28 | {% if key.is_revoked %} | |
| 29 | 29 | <span class="badge badge-error">Revoked</span> | |
| 30 | 30 | {% else %} | |
| 31 | - | <span class="badge badge-success">Active</span> | |
| 31 | + | <span class="badge badge--live">Active</span> | |
| 32 | 32 | {% endif %} | |
| 33 | 33 | </td> | |
| 34 | 34 | <td>{{ key.created_at }}</td> |
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | <tbody> | |
| 19 | 19 | {% for version in versions %} | |
| 20 | 20 | <tr> | |
| 21 | - | <td><span class="badge{% if version.is_current %} current{% endif %}">v{{ version.number }}</span></td> | |
| 21 | + | <td><span class="badge{% if version.is_current %} badge--live{% endif %}">v{{ version.number }}</span></td> | |
| 22 | 22 | <td class="item-version-upload-cell-sm">{% if let Some(label) = version.label %}{{ label }}{% endif %}</td> | |
| 23 | 23 | <td class="item-version-upload-cell-sm">{% if version.has_file %}{% match version.file_name %}{% when Some with (name) %}{{ name }}{% when None %}1 file{% endmatch %}{% else %}<span class="item-version-upload-no-file">No file</span>{% endif %}</td> | |
| 24 | 24 | <td>{{ version.size }}</td> |
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | {% if enabled %} | |
| 4 | 4 | <div class="totp-status-row"> | |
| 5 | - | <span class="badge active">Enabled</span> | |
| 5 | + | <span class="badge badge--live">Enabled</span> | |
| 6 | 6 | </div> | |
| 7 | 7 | ||
| 8 | 8 | <div class="totp-section"> |
| @@ -74,9 +74,29 @@ | |||
| 74 | 74 | }); | |
| 75 | 75 | } | |
| 76 | 76 | ||
| 77 | + | /** Elements the browser already activates from the keyboard. */ | |
| 78 | + | const NATIVELY_ACTIVATED = new Set(['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA']); | |
| 79 | + | ||
| 80 | + | /** Enter / Space on a `data-action` element the browser does not activate on | |
| 81 | + | * its own. The charter requires a focus ring on custom interactive containers | |
| 82 | + | * such as the sort headers, and a ring on something that cannot be operated | |
| 83 | + | * from the keyboard is decoration. Opt in with `tabindex="0"`. */ | |
| 84 | + | function listenKeyActivate(): void { | |
| 85 | + | document.addEventListener('keydown', (evt) => { | |
| 86 | + | const key = (evt as KeyboardEvent).key; | |
| 87 | + | if (key !== 'Enter' && key !== ' ') return; | |
| 88 | + | const el = (evt.target as Element | null)?.closest('[data-action]'); | |
| 89 | + | if (!el || NATIVELY_ACTIVATED.has(el.tagName) || !el.hasAttribute('tabindex')) return; | |
| 90 | + | evt.preventDefault(); | |
| 91 | + | const verb = el.getAttribute('data-action'); | |
| 92 | + | if (verb) run(el, verb, evt); | |
| 93 | + | }); | |
| 94 | + | } | |
| 95 | + | ||
| 77 | 96 | /** Install the `data-action`/`-change`/`-input`/`-submit` dispatcher. */ | |
| 78 | 97 | export function initActionDispatcher(): void { | |
| 79 | 98 | listen('click', 'data-action', false); | |
| 99 | + | listenKeyActivate(); | |
| 80 | 100 | listen('change', 'data-change', false); | |
| 81 | 101 | listen('input', 'data-input', false); | |
| 82 | 102 | listen('submit', 'data-submit', true); |
| @@ -62,12 +62,12 @@ | |||
| 62 | 62 | <tr> | |
| 63 | 63 | <th class="col-3"><input type="checkbox" id="select-all" data-change="toggleSelectAllFromEl"></th> | |
| 64 | 64 | <th class="col-5">#</th> | |
| 65 | - | <th class="col-32 sortable-th" data-action="sortContent" data-arg="2" data-arg2="text" title="Sort by title">Item <span class="sort-arrow" data-col="2"></span></th> | |
| 66 | - | <th class="col-12 sortable-th" data-action="sortContent" data-arg="3" data-arg2="text" title="Sort by type">Type <span class="sort-arrow" data-col="3"></span></th> | |
| 67 | - | <th class="col-10 sortable-th" data-action="sortContent" data-arg="4" data-arg2="money" title="Sort by price">Price <span class="sort-arrow" data-col="4"></span></th> | |
| 68 | - | <th class="col-10 sortable-th" data-action="sortContent" data-arg="5" data-arg2="num" title="Sort by sales">Sales <span class="sort-arrow" data-col="5"></span></th> | |
| 69 | - | <th class="col-10 sortable-th" data-action="sortContent" data-arg="6" data-arg2="money" title="Sort by revenue">Revenue <span class="sort-arrow" data-col="6"></span></th> | |
| 70 | - | <th class="col-8 sortable-th" data-action="sortContent" data-arg="7" data-arg2="text" title="Sort by status">Status <span class="sort-arrow" data-col="7"></span></th> | |
| 65 | + | <th class="col-32 sortable-th" tabindex="0" data-action="sortContent" data-arg="2" data-arg2="text" title="Sort by title">Item <span class="sort-arrow" data-col="2"></span></th> | |
| 66 | + | <th class="col-12 sortable-th" tabindex="0" data-action="sortContent" data-arg="3" data-arg2="text" title="Sort by type">Type <span class="sort-arrow" data-col="3"></span></th> | |
| 67 | + | <th class="col-10 sortable-th" tabindex="0" data-action="sortContent" data-arg="4" data-arg2="money" title="Sort by price">Price <span class="sort-arrow" data-col="4"></span></th> | |
| 68 | + | <th class="col-10 sortable-th" tabindex="0" data-action="sortContent" data-arg="5" data-arg2="num" title="Sort by sales">Sales <span class="sort-arrow" data-col="5"></span></th> | |
| 69 | + | <th class="col-10 sortable-th" tabindex="0" data-action="sortContent" data-arg="6" data-arg2="money" title="Sort by revenue">Revenue <span class="sort-arrow" data-col="6"></span></th> | |
| 70 | + | <th class="col-8 sortable-th" tabindex="0" data-action="sortContent" data-arg="7" data-arg2="text" title="Sort by status">Status <span class="sort-arrow" data-col="7"></span></th> | |
| 71 | 71 | <th class="col-10">Actions</th> | |
| 72 | 72 | </tr> | |
| 73 | 73 | </thead> |
| @@ -76,9 +76,9 @@ | |||
| 76 | 76 | <td class="proj-members-split-cell">{{ member.split_percent }}%</td> | |
| 77 | 77 | <td> | |
| 78 | 78 | {% if member.stripe_connected %} | |
| 79 | - | <span class="badge active">Connected</span> | |
| 79 | + | <span class="badge badge--live">Connected</span> | |
| 80 | 80 | {% else %} | |
| 81 | - | <span class="badge pending">Not connected</span> | |
| 81 | + | <span class="badge badge--pending">Not connected</span> | |
| 82 | 82 | {% endif %} | |
| 83 | 83 | </td> | |
| 84 | 84 | <td class="text-xs">{{ member.added_at }}</td> |