Draft: Merge status into feedback design tokens

Background

Feedback and status design token categories have historically had less than ideal separation in definition and use than other design token categories. So much so that we have dedicated documentation on deciding between the two.

Proposal

  • Combine status and feedback design tokens into a single feedback category.
  • Add border design tokens to support more use cases.
  • Update feedback definition to encompass a range of meaning from things that happen as a result of something else to things that are in a particular state of meaning.

Visual updates TBD…

Considerations

Constants used in status and feeback

Status Token Color Constants

Status Type Element Light Mode Dark Mode
Neutral Background {color.neutral.100} {color.neutral.800}
Text {color.neutral.700} {color.neutral.200}
Icon {color.neutral.500} {color.neutral.300}
Info Background {color.blue.100} {color.blue.800}
Text {color.blue.700} {color.blue.200}
Icon {color.blue.500} {color.blue.300}
Success Background {color.green.100} {color.green.800}
Text {color.green.700} {color.green.200}
Icon {color.green.500} {color.green.300}
Warning Background {color.orange.100} {color.orange.800}
Text {color.orange.700} {color.orange.200}
Icon {color.orange.500} {color.orange.300}
Danger Background {color.red.100} {color.red.800}
Text {color.red.700} {color.red.200}
Icon {color.red.500} {color.red.300}
Brand Background {color.purple.100} {color.purple.900}
Text {color.purple.700} {color.purple.300}
Icon {color.purple.500} {color.purple.500}

Feedback Token Color Constants

Feedback Type Element Light Mode Dark Mode
Neutral Background {color.neutral.50} {color.neutral.900}
Text {color.neutral.700} {color.neutral.200}
Icon {color.neutral.600} {color.neutral.300}
Info Background {color.blue.50} {color.blue.950}
Text {color.blue.700} {color.blue.200}
Icon {color.blue.600} {color.blue.300}
Success Background {color.green.50} {color.green.900}
Text {color.green.700} {color.green.200}
Icon {color.green.600} {color.green.300}
Warning Background {color.orange.50} {color.orange.900}
Text {color.orange.700} {color.orange.200}
Icon {color.orange.600} {color.orange.300}
Danger Background {color.red.50} {color.red.900}
Text {color.red.700} {color.red.200}
Icon {color.red.600} {color.red.300}
Strong Background {color.neutral.800} (no dark variant)
Text {color.neutral.0} (no dark variant)
Icon {color.neutral.0} (no dark variant)
Link {color.blue.300} (no dark variant)

Key overlaps

Complete overlaps (same color constant used)

Text colors for all types are identical in both systems:

  • Neutral, Info, Success, Warning, Danger all use {color.[type].700} in light mode
  • All use {color.[type].200} in dark mode

Icon colors in dark mode are identical:

  • All use {color.[type].300} for their respective types

Partial overlaps

  • Neutral icon in dark mode: Both use {color.neutral.300}
  • Blue/Info icon in dark mode: Both use {color.blue.300}
  • Green/Success icon in dark mode: Both use {color.green.300}
  • Orange/Warning icon in dark mode: Both use {color.orange.300}
  • Red/Danger icon in dark mode: Both use {color.red.300}

Key differences

  1. Background colors
    • Status: Uses stronger backgrounds - 100 (light) / 800 (dark)
    • Feedback: Uses lighter backgrounds - 50 (light) / 900-950 (dark)
    • Result: Status is more prominent/saturated for indicating ongoing states
  2. Icon colors in light mode
    • Status: Uses 500 level colors (more saturated)
    • Feedback: Uses 600 level colors (slightly darker)
  3. Neutral backgrounds
    • Status: neutral.100 / neutral.800
    • Feedback: neutral.50 / neutral.900
    • Result: Feedback is lighter than status in light mode, and darker than status in dark mode
  4. Special variants
    • Status: Has a Brand variant using purple colors (no use of the brand variant turned up in the results below)
    • Feedback: Has a Strong variant with inverted high-contrast colors
    • Note: Strong feedback doesn't have dark mode variants

Additional design tokens

Feedback-specific

  • feedback.border.radius: {border.radius.lg} - Border radius for feedback elements

Current status usage

Usage in the monolith identified by git grep -E "gl-[a-zA-Z-]*status[a-zA-Z-]*(success|warning|danger|info|neutral)" | cat (not including specs or other use of "status," like illustrations).

Monolith

Known uses…
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    backgroundClass: 'gl-bg-status-success',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    foregroundClass: 'gl-fill-status-success',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    backgroundClass: 'gl-bg-status-danger',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    foregroundClass: 'gl-fill-status-danger',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    backgroundClass: 'gl-bg-status-info',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    foregroundClass: 'gl-fill-status-info',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    backgroundClass: 'gl-bg-status-neutral',
app/assets/javascripts/merge_request_dashboard/components/user_avatar.vue:    foregroundClass: 'gl-fill-status-neutral',
app/assets/javascripts/notes/components/multiline_comment_utils.js:      'gl-bg-status-success gl-text-status-success': symbol === '+',
app/assets/javascripts/notes/components/multiline_comment_utils.js:      'gl-bg-status-danger gl-text-status-danger': symbol === '-',
app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue:        :class="{ '!gl-text-status-info': subscribed }"
app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue:        :class="{ '!gl-text-status-info': subscribed }"
app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue:        :class="{ '!gl-text-status-info': hasTodo }"
app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue:        :class="{ '!gl-text-status-info': hasTodo }"
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-danger',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-danger',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-warning',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-warning',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-success',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-success',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-neutral',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-neutral',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-danger',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-danger',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-neutral',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-neutral',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-info',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-info',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-info',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-info',
app/assets/javascripts/vue_merge_request_widget/constants.js:    backgroundClass: 'gl-bg-status-neutral',
app/assets/javascripts/vue_merge_request_widget/constants.js:    iconClass: 'gl-fill-status-neutral',
app/assets/javascripts/whats_new/components/feature.vue:            class="gl-my-1 gl-mr-1 gl-text-status-info"
app/assets/javascripts/wikis/components/wiki_header.vue:            :class="{ '!gl-text-status-info': wikiPage.subscribed }"
app/assets/javascripts/wikis/components/wiki_sidebar_entry.vue:        (match) => `<span class="gl-bg-status-warning">${match}</span>`,
app/assets/javascripts/work_items/components/shared/todos_toggle.vue:      :class="{ '!gl-text-status-info': pendingTodo }"
app/assets/javascripts/work_items/components/work_item_notifications_widget.vue:      :class="{ '!gl-text-status-info': workItemNotificationsSubscribed }"
app/assets/stylesheets/framework/animations.scss:    background-color: var(--gl-status-info-icon-color);
app/assets/stylesheets/framework/animations.scss:    background-color: rgb(from var(--gl-status-info-icon-color) r g b / 0.7);
app/assets/stylesheets/framework/diffs.scss:        border: 1px solid var(--gl-status-danger-icon-color);
app/assets/stylesheets/framework/diffs.scss:        border: 1px solid var(--gl-status-success-icon-color);
app/assets/stylesheets/framework/diffs.scss:      border: 1px solid var(--gl-status-danger-icon-color);
app/assets/stylesheets/framework/diffs.scss:      border: 1px solid var(--gl-status-success-icon-color);
app/assets/stylesheets/framework/sidebar.scss:  color: var(--gl-status-warning-icon-color);
app/assets/stylesheets/page_bundles/design_management.scss:    background-color: var(--gl-status-warning-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:  background-color: var(--gl-status-neutral-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-color: var(--gl-status-success-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-background-color: var(--gl-status-success-background-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-color: var(--gl-status-danger-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-background-color: var(--gl-status-danger-background-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-color: var(--gl-status-info-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-background-color: var(--gl-status-info-background-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-color: var(--gl-status-warning-icon-color);
app/assets/stylesheets/page_bundles/notes/_system_notes.scss:    --system-note-icon-background-color: var(--gl-status-warning-background-color);
app/assets/stylesheets/page_bundles/profile.scss:      fill: var(--gl-status-neutral-icon-color) !important;
app/assets/stylesheets/page_bundles/work_item_settings.scss:    background-color: var(--gl-status-info-icon-color);
app/assets/stylesheets/page_bundles/work_item_settings.scss:    background-color: var(--gl-status-info-icon-color);
app/assets/stylesheets/page_bundles/work_item_settings.scss:    box-shadow: 0 0 0 2px var(--gl-status-info-icon-color);
app/assets/stylesheets/page_bundles/work_item_settings.scss:    background-color: var(--gl-status-info-icon-color);
app/assets/stylesheets/page_bundles/work_item_settings.scss:    border: 1px solid var(--gl-status-neutral-icon-color);
app/assets/stylesheets/pages/events.scss:        @apply gl-fill-status-success;
app/assets/stylesheets/pages/events.scss:      @apply gl-fill-status-danger;
app/assets/stylesheets/pages/events.scss:      @apply gl-fill-status-info;
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-green-100 dark:gl-border-green-700 gl-bg-status-success';
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-red-100 dark:gl-border-red-700 gl-bg-status-danger';
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-blue-100 dark:gl-border-blue-700 gl-bg-status-info';
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-orange-100 dark:gl-border-orange-700 gl-bg-status-warning';
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-neutral-100 dark:gl-border-neutral-100 gl-bg-status-neutral';
ee/app/assets/javascripts/ai/duo_agents_platform/components/common/agent_status_icon.vue:          return 'gl-border-neutral-100 dark:gl-border-neutral-700 gl-bg-status-neutral';
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/details_drawer.vue:          <span v-if="status.failCount" class="gl-text-status-danger">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/details_drawer.vue:          <span v-if="status.pendingCount" class="gl-text-status-neutral">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/details_drawer.vue:          <span v-if="status.passCount" class="gl-text-status-success">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/statuses_list.vue:        <span v-if="controlStatus.status === 'FAIL'" class="gl-text-status-danger">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/statuses_list.vue:          <span v-if="getControlStatusFixes(controlStatus).length" class="gl-text-status-neutral">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/statuses_list.vue:        <span v-if="controlStatus.status === 'PENDING'" class="gl-text-status-neutral">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/statuses_list.vue:        <span v-if="controlStatus.status === PASS_STATUS" class="gl-text-status-success">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/grouped_table/grouped_table.vue:            <span class="gl-text-status-danger">
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          text: 'gl-text-status-danger',
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          fill: 'gl-fill-status-danger',
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          text: 'gl-text-status-neutral',
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          fill: 'gl-fill-status-neutral',
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          text: 'gl-text-status-success',
ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/requirement_status.vue:          fill: 'gl-fill-status-success',
ee/app/assets/javascripts/security_configuration/components/security_attributes/category_list.vue:      class="gl-my-1 gl-flex gl-cursor-pointer gl-items-center gl-rounded-base gl-p-3 hover:!gl-bg-status-neutral"
ee/app/assets/javascripts/security_inventory/components/group_tool_coverage_indicator.vue:        class="gl-text-sm gl-text-status-neutral"
ee/app/assets/javascripts/security_inventory/components/sidebar/expandable_group.vue:      class="gl-relative gl-m-1 gl-flex gl-h-8 gl-cursor-pointer gl-items-center gl-gap-4 gl-rounded-base gl-px-3 hover:!gl-bg-status-neutral"
ee/app/assets/javascripts/security_inventory/components/sidebar/subgroup_sidebar.vue:          class="gl-relative gl-ml-1 gl-mt-6 gl-flex gl-h-8 gl-cursor-pointer gl-items-center gl-gap-4 gl-rounded-base gl-px-3 hover:!gl-bg-status-neutral"
ee/app/assets/javascripts/security_inventory/components/vulnerability_indicator.vue:    <span class="gl-text-sm gl-text-status-neutral">
ee/app/views/projects/issues/_health_status.html.haml:- status_classes = { 'at_risk' => 'gl-bg-status-danger gl-text-status-danger', 'on_track' => 'gl-bg-status-success gl-text-status-success', 'needs_attention' => 'gl-bg-status-warning gl-text-status-warning' }
Usage breakdown…
  • 94 total instances
  • 24 unique classes/variables
  • Most used: `--gl-status-info-icon-color (10)

Status class use

Pajamas

Known uses…

contents/product-foundations/design-tokens-using.md:

contents/product-foundations/design-tokens-using.md: contents/product-foundations/design-tokens-using.md:
contents/product-foundations/design-tokens-using.md: contents/product-foundations/design-tokens-using.md:
contents/product-foundations/design-tokens-using.md: contents/product-foundations/design-tokens-using.md:
contents/product-foundations/design-tokens-using.md: contents/product-foundations/design-tokens-using.md:
contents/product-foundations/design-tokens-using.md: contents/product-foundations/layout.md:
packages/gitlab-ui/src/components/base/avatars_inline/avatars_inline.scss: background-color: var(--gl-status-neutral-background-color); packages/gitlab-ui/src/components/base/avatars_inline/avatars_inline.scss: color: var(--gl-status-neutral-text-color); packages/gitlab-ui/src/components/base/markdown/markdown.scss: @apply gl-bg-status-danger; packages/gitlab-ui/src/components/base/markdown/markdown.scss: @apply gl-bg-status-success; packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue:
packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue:
packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue: class="dashboard-card-skeleton-info gl-mb-3 gl-w-full gl-bg-status-neutral gl-py-3" packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue:
packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue:
packages/gitlab-ui/src/components/regions/dashboard_skeleton/dashboard_skeleton.vue:
packages/gitlab-ui/src/directives/resize_observer/resize_observer.stories.js: class="gl-flex gl-relative gl-justify-center gl-items-center gl-bg-status-neutral gl-text-default">
Edited by Jeremy Elder