GlTable in busy state violates minimum color contrast
## Problem Storybook accessibility tests on the gitlab repository site revealed that `GlTable` with `busy` prop passed violates the color contrast rule. BootstrapVue styles busy tables with `opacity: 0.55`, which reduces foreground-to-background contrast below acceptable levels for text content. **Note:** While WCAG exempts disabled elements from minimum contrast rules, GitLab aims for at least 3:1 for disabled items per `Pajamas visual accessibility guidelines`. Some combinations falling below that is acceptable for a temporary state. ## Context ### GlTable busy How `busy` works today for GlTable: * Sets `aria-busy="true"` on the table element * Applies `opacity: 0.55` to the entire table via CSS * If a `#table-busy` slot is provided AND `busy="true"`, the slot content replaces all item rows * Without the slot, rows remain visible but faded with no loading indicator * GlTable wraps BootstrapVue's BTable and passes `busy` through via `v-bind="$attrs"` ### Busy usage across GitLab Audit completed in #3307 7 files use the `:busy` prop (52 actual tables, 5 non-table/Storybook files): | Pattern | Count | % | |---------|-------|---| | `GlLoadingIcon` in `#table-busy` slot | 31 | 54% | | `GlSkeletonLoader` in `#table-busy` slot | 9 | 16% | | Default GlTable behavior (no custom slot) | 9 | 16% | | Non-table components | 8 | 14% | Additionally, 14+ tables use conditional rendering (`v-if/v-else`) instead of the `busy` prop. ### Loading state design across the design system Spinner is the standard Across the design system, we have components that have a loading state built in. Although we do have a skeleton loader, most components use a version of the spinner. https://gitlab.com/gitlab-org/gitlab-services/design.gitlab.com/-/issues/3156#note_3109290243 ## Proposed solution The default loading or busy state for GlTable should include a spinner to communicate with users in a way consistent with the rest of the design system. A default loading state of replacing content with a spinner is reasonable. Doing so gives an equitable experience for all users, and has no risk of mistaking table content for current or interactive. ### Challenges There are many existing implementations, and the solution here should support them. What is the path for content that is currently designed to remain visible? With the full opacity these no longer communicate disabled.
issue