refactor(web): resolve CSS important rule violations (#5932)

This commit is contained in:
Dave Lane authored and GitHub committed 2026-10-14 20:24:16 -04:00
1 parent a178f465e4
commit 2e4d4f5ac0
4 files changed
+313 -100

No files matched your search

+22 -19
View File
@@ -8,24 +8,27 @@
<!-- Category Filter -->
<div class="toolbar mb-4">
<fieldset class="btn-group">
<fieldset>
<legend class="visually-hidden">{{ $t('featured.category_filter') }}</legend>
<button
type="button"
class="btn btn-outline-primary"
:class="{ active: selectedCategory === null }"
@click="selectedCategory = null">
{{ $t('_common.all') }}
</button>
<button
v-for="category in categories"
:key="category.id"
type="button"
class="btn btn-outline-primary"
:class="{ active: selectedCategory === category.id }"
@click="selectedCategory = category.id">
{{ $t(`featured.categories.${category.originalId}`) }}
</button>
<!-- @brief Keep the legend outside Bootstrap's button group to preserve its outer corners. -->
<div class="btn-group">
<button
type="button"
class="btn btn-outline-primary"
:class="{ active: selectedCategory === null }"
@click="selectedCategory = null">
{{ $t('_common.all') }}
</button>
<button
v-for="category in categories"
:key="category.id"
type="button"
class="btn btn-outline-primary"
:class="{ active: selectedCategory === category.id }"
@click="selectedCategory = category.id">
{{ $t(`featured.categories.${category.originalId}`) }}
</button>
</div>
</fieldset>
</div>
@@ -67,7 +70,7 @@
<h5 class="card-title mb-1">{{ app.name }}</h5>
<p class="text-muted small mb-0" v-if="app.tagline">{{ app.tagline }}</p>
</div>
<span v-if="app.official" class="badge bg-primary flex-shrink-0 ms-2">{{ $t('featured.official') }}</span>
<span v-if="app.official" class="badge badge-primary flex-shrink-0 ms-2">{{ $t('featured.official') }}</span>
</div>
<p class="card-text text-muted small mb-3">{{ app.description }}</p>
@@ -117,7 +120,7 @@
<!-- Platform Icons -->
<div class="mb-3">
<span class="badge bg-secondary me-1" v-for="platform in app.platforms" :key="platform">
<span class="badge badge-secondary me-1" v-for="platform in app.platforms" :key="platform">
<smartphone v-if="platform === 'android'" :size="14" class="icon"></smartphone>
<gamepad2 v-else-if="platform === 'console'" :size="14" class="icon"></gamepad2>
<gamepad v-else-if="platform === 'handheld'" :size="14" class="icon"></gamepad>
+3 -3
View File
@@ -122,8 +122,8 @@ export default {
</script>
<style>
/* Navbar toggler icon for dark text on light background */
.navbar-sunshine .navbar-toggler-icon {
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
/** @brief Keep the Sunshine toggler icon above Bootstrap's light and dark variants. */
.navbar-sunshine .navbar-toggler .navbar-toggler-icon {
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
</style>
+104 -78
View File
@@ -2,6 +2,13 @@
Sunshine UI - Modern Design System
========================================================================== */
/**
* @brief Theme Bootstrap components through source order and component selectors.
*
* Load this stylesheet after Bootstrap. Use badge-primary and badge-secondary
* for themed badges so Bootstrap background utilities do not override them.
*/
/* Hide pages while localization is loading */
[v-cloak] {
display: none;
@@ -969,8 +976,8 @@ html {
}
body {
background-color: var(--color-bg-base) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-base);
color: var(--color-text-base);
transition: background-color var(--transition-slow), color var(--transition-slow);
min-height: 100vh;
}
@@ -993,11 +1000,11 @@ body {
}
.card {
background-color: var(--color-surface) !important;
border: 1px solid var(--color-border) !important;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
color: var(--color-text-base) !important;
color: var(--color-text-base);
transition: all var(--transition-base);
}
@@ -1006,109 +1013,123 @@ body {
}
.card-header {
background-color: var(--color-bg-subtle) !important;
border-color: var(--color-border) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-subtle);
border-color: var(--color-border);
color: var(--color-text-base);
}
.card-footer {
background-color: var(--color-bg-subtle) !important;
border-color: var(--color-border) !important;
background-color: var(--color-bg-subtle);
border-color: var(--color-border);
}
.modal-content {
background-color: var(--color-surface) !important;
border-color: var(--color-border) !important;
background-color: var(--color-surface);
border-color: var(--color-border);
}
.modal-header,
.modal-footer {
border-color: var(--color-border) !important;
background-color: var(--color-surface-raised) !important;
border-color: var(--color-border);
background-color: var(--color-surface-raised);
}
.list-group-item {
background-color: var(--color-surface) !important;
border-color: var(--color-border) !important;
color: var(--color-text-base) !important;
--bs-list-group-disabled-bg: var(--color-surface);
--bs-list-group-disabled-color: var(--color-text-base);
--bs-list-group-action-hover-bg: var(--color-surface);
--bs-list-group-action-hover-color: var(--color-text-base);
--bs-list-group-action-active-bg: var(--color-surface);
--bs-list-group-action-active-color: var(--color-text-base);
--bs-list-group-active-bg: var(--color-surface);
--bs-list-group-active-border-color: var(--color-border);
--bs-list-group-active-color: var(--color-text-base);
background-color: var(--color-surface);
border-color: var(--color-border);
color: var(--color-text-base);
}
.list-group-item:hover {
background-color: var(--color-bg-subtle) !important;
.list-group-item:hover,
.list-group-item-action:not(.active):hover {
background-color: var(--color-bg-subtle);
}
.accordion-item {
background-color: var(--color-surface) !important;
border-color: var(--color-border) !important;
background-color: var(--color-surface);
border-color: var(--color-border);
}
.accordion-button {
background-color: var(--color-bg-subtle) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-subtle);
color: var(--color-text-base);
}
.accordion-button:not(.collapsed) {
background-color: var(--color-bg-muted) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-muted);
color: var(--color-text-base);
}
.offcanvas {
background-color: var(--color-surface) !important;
background-color: var(--color-surface);
}
.offcanvas-header,
.offcanvas-body {
color: var(--color-text-base) !important;
color: var(--color-text-base);
}
.toast {
background-color: var(--color-surface) !important;
border-color: var(--color-border) !important;
background-color: var(--color-surface);
border-color: var(--color-border);
}
.toast-header {
background-color: var(--color-bg-subtle) !important;
border-color: var(--color-border) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-subtle);
border-color: var(--color-border);
color: var(--color-text-base);
}
.toast-body {
color: var(--color-text-base) !important;
color: var(--color-text-base);
}
.popover {
background-color: var(--color-surface) !important;
border-color: var(--color-border) !important;
background-color: var(--color-surface);
border-color: var(--color-border);
}
.popover-header {
background-color: var(--color-bg-subtle) !important;
border-color: var(--color-border) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-subtle);
border-color: var(--color-border);
color: var(--color-text-base);
}
.popover-body {
color: var(--color-text-base) !important;
color: var(--color-text-base);
}
.tooltip-inner {
background-color: var(--color-bg-muted) !important;
color: var(--color-text-base) !important;
background-color: var(--color-bg-muted);
color: var(--color-text-base);
}
.breadcrumb {
background-color: var(--color-bg-subtle) !important;
background-color: var(--color-bg-subtle);
}
.breadcrumb-item a {
color: var(--color-primary) !important;
color: var(--color-primary);
}
.breadcrumb-item.active {
color: var(--color-text-muted) !important;
color: var(--color-text-muted);
}
.pagination {
--bs-pagination-hover-color: var(--color-text-base);
--bs-pagination-focus-color: var(--color-text-base);
--bs-pagination-active-color: var(--color-text-base);
--bs-pagination-disabled-color: var(--color-text-base);
--bs-pagination-bg: var(--color-surface);
--bs-pagination-border-color: var(--color-border);
--bs-pagination-hover-bg: var(--color-bg-subtle);
@@ -1119,7 +1140,7 @@ body {
}
.page-link {
color: var(--color-text-base) !important;
color: var(--color-text-base);
}
/* ==========================================================================
@@ -1127,7 +1148,8 @@ body {
========================================================================== */
.navbar-sunshine {
background: var(--navbar-bg) !important;
--bs-navbar-nav-link-padding-x: 1rem;
background: var(--navbar-bg);
box-shadow: var(--shadow-md);
border: none;
transition: all var(--transition-base);
@@ -1142,9 +1164,9 @@ body {
}
.navbar-sunshine .nav-link {
color: var(--navbar-text-muted) !important;
color: var(--navbar-text-muted);
font-weight: 500;
padding: 0.5rem 1rem !important;
padding: 0.5rem 1rem;
min-height: calc(1.5em + 1rem);
border-radius: var(--radius-md);
transition: all var(--transition-fast);
@@ -1154,25 +1176,25 @@ body {
}
.navbar-sunshine .nav-link:hover {
color: var(--navbar-text) !important;
color: var(--navbar-text);
background-color: rgba(255, 255, 255, 0.1);
transform: translateY(-1px);
}
.navbar-sunshine .nav-link.active {
color: var(--navbar-text) !important;
color: var(--navbar-text);
background-color: rgba(255, 255, 255, 0.15);
font-weight: 600;
}
.navbar-sunshine .nav-link.dropdown-toggle.show {
color: var(--navbar-text) !important;
color: var(--navbar-text);
background-color: rgba(255, 255, 255, 0.15);
}
.navbar-sunshine .navbar-toggler {
color: var(--navbar-text) !important;
border-color: rgba(255, 255, 255, 0.2) !important;
color: var(--navbar-text);
border-color: rgba(255, 255, 255, 0.2);
}
.navbar-sunshine .navbar-toggler:focus {
@@ -1321,7 +1343,7 @@ p {
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
transform: none;
}
.btn-primary {
@@ -1420,14 +1442,14 @@ p {
Badge Styles
========================================================================== */
.badge.bg-primary {
background-color: var(--color-primary) !important;
color: var(--color-on-primary) !important;
.badge.badge-primary {
background-color: var(--color-primary);
color: var(--color-on-primary);
}
.badge.bg-secondary {
background-color: var(--color-bg-muted) !important;
color: var(--color-text-base) !important;
.badge.badge-secondary {
background-color: var(--color-bg-muted);
color: var(--color-text-base);
}
/* ==========================================================================
@@ -2242,7 +2264,7 @@ p {
}
.cover-finder .cover-results.busy * {
cursor: wait !important;
cursor: wait;
pointer-events: none;
}
@@ -2313,8 +2335,8 @@ p {
.markdown-body.release-notes {
width: 100%;
max-width: 100%;
column-count: 1 !important;
display: block !important;
column-count: 1;
display: block;
}
.markdown-body h1,
@@ -2432,8 +2454,8 @@ p {
========================================================================== */
.dropdown-menu {
background-color: var(--color-surface) !important;
border: 1px solid var(--color-border) !important;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
padding: var(--spacing-sm);
@@ -2446,27 +2468,31 @@ p {
.dropdown-item {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
color: var(--color-text-base) !important;
--bs-dropdown-link-hover-color: var(--color-text-base);
--bs-dropdown-link-active-color: var(--color-text-base);
--bs-dropdown-link-disabled-color: var(--color-text-base);
color: var(--color-text-base);
transition: all var(--transition-fast);
}
.dropdown-item:hover {
background-color: var(--color-bg-muted) !important;
color: var(--color-text-base) !important;
}
.dropdown-item.active {
background-color: var(--color-primary-light);
color: var(--color-primary);
color: var(--color-text-base);
}
[data-bs-theme="dark"] .dropdown-item.active {
background-color: var(--color-primary);
color: white;
color: var(--color-text-base);
}
.dropdown-item:hover,
.dropdown-menu .dropdown-item:hover {
background-color: var(--color-bg-muted);
color: var(--color-text-base);
}
.dropdown-divider {
border-color: var(--color-border) !important;
border-color: var(--color-border);
}
/* Add spacing between icons and text in dropdown items */
@@ -2705,8 +2731,8 @@ p {
}
.featured-app-icon img {
max-width: 64px !important;
max-height: 64px !important;
max-width: 64px;
max-height: 64px;
width: auto;
height: auto;
object-fit: contain;
@@ -2791,9 +2817,9 @@ p {
}
.screenshot-thumbnail {
height: 120px !important;
height: 120px;
width: auto;
max-width: 200px !important;
max-width: 200px;
min-width: 100px;
border-radius: var(--radius-sm);
cursor: pointer;
+184
View File
@@ -0,0 +1,184 @@
import { mount, flushPromises } from '@vue/test-utils'
import { parse } from '@vue/compiler-sfc'
import { readFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import navbarSource from '../../src_assets/common/assets/web/Navbar.vue?raw'
vi.mock('../../src_assets/common/assets/web/Navbar.vue', () => ({
default: { template: '<div />' },
}))
import Featured from '../../src_assets/common/assets/web/Featured.vue'
const navbarCss = parse(navbarSource).descriptor.styles[0].content
const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
const bootstrapCss = readFileSync(resolve(projectRoot, 'node_modules/bootstrap/dist/css/bootstrap.css'), 'utf8')
const sunshineCss = readFileSync(resolve(projectRoot, 'src_assets/common/assets/web/sunshine.css'), 'utf8')
let stylesheet
let wrapper
beforeAll(() => {
stylesheet = document.createElement('style')
stylesheet.textContent = [bootstrapCss, sunshineCss, navbarCss].join('\n')
document.head.append(stylesheet)
})
afterEach(() => {
wrapper?.unmount()
wrapper = undefined
document.body.replaceChildren()
delete document.documentElement.dataset.theme
delete document.documentElement.dataset.bsTheme
vi.unstubAllGlobals()
})
afterAll(() => {
stylesheet.remove()
})
/**
* @brief Read the winning style with Bootstrap and the Sunshine theme loaded.
*
* @param {string} markup Component markup to insert into the document.
* @param {string} selector Element whose computed styles should be returned.
* @returns {CSSStyleDeclaration} Computed style, including unresolved theme variables in jsdom.
*/
function componentStyle(markup, selector) {
document.body.innerHTML = markup
return getComputedStyle(document.querySelector(selector))
}
/**
* @brief Mount the featured page with a deterministic app directory.
*
* @param {object} directory App and category data returned by the directory service.
* @returns {Promise<import('@vue/test-utils').VueWrapper>} Mounted featured page.
*/
async function mountFeatured(directory) {
vi.stubGlobal('fetch', vi.fn(async () => ({
ok: true,
json: async () => directory,
})))
wrapper = mount(Featured, {
attachTo: document.body,
global: { mocks: { $t: key => key }, stubs: { Navbar: true, SimpleIcon: true } },
})
await flushPromises()
return wrapper
}
const componentCases = [
['body', '<div></div>', 'body', { backgroundColor: 'var(--color-bg-base)', color: 'var(--color-text-base)' }],
['card', '<div class="card"></div>', '.card', { backgroundColor: 'var(--color-surface)', color: 'var(--color-text-base)' }],
['card header', '<div class="card-header"></div>', '.card-header', { backgroundColor: 'var(--color-bg-subtle)', color: 'var(--color-text-base)' }],
['card footer', '<div class="card-footer"></div>', '.card-footer', { backgroundColor: 'var(--color-bg-subtle)' }],
['modal', '<div class="modal-content"></div>', '.modal-content', { backgroundColor: 'var(--color-surface)' }],
['modal header', '<div class="modal-header"></div>', '.modal-header', { backgroundColor: 'var(--color-surface-raised)' }],
['modal footer', '<div class="modal-footer"></div>', '.modal-footer', { backgroundColor: 'var(--color-surface-raised)' }],
['list item', '<div class="list-group-item"></div>', '.list-group-item', { backgroundColor: 'var(--color-surface)', color: 'var(--color-text-base)' }],
['accordion', '<div class="accordion-item"></div>', '.accordion-item', { backgroundColor: 'var(--color-surface)' }],
['collapsed accordion button', '<button class="accordion-button collapsed"></button>', 'button', { backgroundColor: 'var(--color-bg-subtle)', color: 'var(--color-text-base)' }],
['expanded accordion button', '<button class="accordion-button"></button>', 'button', { backgroundColor: 'var(--color-bg-muted)', color: 'var(--color-text-base)' }],
['offcanvas', '<div class="offcanvas"></div>', '.offcanvas', { backgroundColor: 'var(--color-surface)' }],
['offcanvas header', '<div class="offcanvas-header"></div>', '.offcanvas-header', { color: 'var(--color-text-base)' }],
['offcanvas body', '<div class="offcanvas-body"></div>', '.offcanvas-body', { color: 'var(--color-text-base)' }],
['toast', '<div class="toast"></div>', '.toast', { backgroundColor: 'var(--color-surface)' }],
['toast header', '<div class="toast-header"></div>', '.toast-header', { backgroundColor: 'var(--color-bg-subtle)', color: 'var(--color-text-base)' }],
['toast body', '<div class="toast-body"></div>', '.toast-body', { color: 'var(--color-text-base)' }],
['popover', '<div class="popover"></div>', '.popover', { backgroundColor: 'var(--color-surface)' }],
['popover header', '<div class="popover-header"></div>', '.popover-header', { backgroundColor: 'var(--color-bg-subtle)', color: 'var(--color-text-base)' }],
['popover body', '<div class="popover-body"></div>', '.popover-body', { color: 'var(--color-text-base)' }],
['tooltip', '<div class="tooltip-inner"></div>', '.tooltip-inner', { backgroundColor: 'var(--color-bg-muted)', color: 'var(--color-text-base)' }],
['breadcrumb', '<ol class="breadcrumb"></ol>', '.breadcrumb', { backgroundColor: 'var(--color-bg-subtle)' }],
['breadcrumb link', '<ol class="breadcrumb"><li class="breadcrumb-item"><a href="#">Page</a></li></ol>', 'a', { color: 'var(--color-primary)' }],
['active breadcrumb', '<ol class="breadcrumb"><li class="breadcrumb-item active">Page</li></ol>', '.breadcrumb-item', { color: 'var(--color-text-muted)' }],
['page link', '<ul class="pagination"><li class="page-item"><a class="page-link">1</a></li></ul>', '.page-link', { color: 'var(--color-text-base)' }],
['navbar link', '<nav class="navbar navbar-expand-lg navbar-sunshine"><ul class="navbar-nav"><li><a class="nav-link">Page</a></li></ul></nav>', '.nav-link', { color: 'var(--navbar-text-muted)', paddingTop: '8px', paddingRight: '16px', paddingBottom: '8px', paddingLeft: '16px' }],
['simple navbar theme control', '<nav class="navbar navbar-sunshine"><button class="nav-link">Theme</button></nav>', 'button', { color: 'var(--navbar-text-muted)', paddingTop: '8px', paddingRight: '16px', paddingBottom: '8px', paddingLeft: '16px' }],
['active navbar link', '<nav class="navbar-sunshine"><ul class="navbar-nav"><li><a class="nav-link active">Page</a></li></ul></nav>', '.nav-link', { color: 'var(--navbar-text)' }],
['open navbar dropdown', '<nav class="navbar-sunshine"><ul class="navbar-nav"><li><button class="nav-link dropdown-toggle show">Menu</button></li></ul></nav>', 'button', { color: 'var(--navbar-text)' }],
['navbar toggler', '<nav class="navbar-sunshine"><button class="navbar-toggler"></button></nav>', 'button', { color: 'var(--navbar-text)', borderTopColor: 'rgba(255, 255, 255, 0.2)' }],
['disabled button', '<button class="btn btn-primary" disabled>Save</button>', 'button', { transform: 'none' }],
['busy cover', '<div class="cover-finder"><div class="cover-results busy"><button class="cover-container result"></button></div></div>', 'button', { cursor: 'wait', pointerEvents: 'none' }],
['release notes', '<div class="markdown-body release-notes"></div>', '.release-notes', { columnCount: '1', display: 'block' }],
['dropdown', '<div class="dropdown-menu"></div>', '.dropdown-menu', { backgroundColor: 'var(--color-surface)' }],
['dropdown item', '<div class="dropdown-menu"><button class="dropdown-item">Theme</button></div>', 'button', { color: 'var(--color-text-base)' }],
['active dropdown item', '<div class="dropdown-menu"><button class="dropdown-item active">Theme</button></div>', 'button', { color: 'var(--color-text-base)' }],
['dropdown divider', '<div class="dropdown-menu"><hr class="dropdown-divider"></div>', 'hr', { opacity: '1' }],
['featured icon', '<div class="featured-app-icon"><img alt="App"></div>', 'img', { maxWidth: '64px', maxHeight: '64px' }],
['screenshot', '<button class="screenshot-thumbnail-button"><img class="screenshot-thumbnail" alt="Screenshot"></button>', 'img', { height: '120px', maxWidth: '200px' }],
]
describe.each(['light', 'dark'])('%s theme cascade', theme => {
it.each([0, 1, 2])('rounds the outside category filter corners with %i categories', async categoryCount => {
document.documentElement.dataset.theme = theme
document.documentElement.dataset.bsTheme = theme
const categories = [
{ id: 'clients', originalId: 'clients' },
{ id: 'tools', originalId: 'tools' },
].slice(0, categoryCount)
await mountFeatured({ apps: [], categories })
expect(wrapper.get('fieldset legend').text()).toBe('featured.category_filter')
const buttons = wrapper.findAll('.btn-group > .btn')
expect(buttons).toHaveLength(categoryCount + 1)
const firstStyle = getComputedStyle(buttons[0].element)
const lastStyle = getComputedStyle(buttons.at(-1).element)
expect(firstStyle.borderTopLeftRadius).not.toBe('0px')
expect(firstStyle.borderBottomLeftRadius).not.toBe('0px')
expect(lastStyle.borderTopRightRadius).not.toBe('0px')
expect(lastStyle.borderBottomRightRadius).not.toBe('0px')
if (categoryCount > 0) {
expect(firstStyle.borderTopRightRadius).toBe('0px')
expect(firstStyle.borderBottomRightRadius).toBe('0px')
expect(lastStyle.borderTopLeftRadius).toBe('0px')
expect(lastStyle.borderBottomLeftRadius).toBe('0px')
for (const button of buttons.slice(1, -1)) {
const style = getComputedStyle(button.element)
expect([style.borderTopLeftRadius, style.borderBottomLeftRadius, style.borderTopRightRadius, style.borderBottomRightRadius]).toEqual(['0px', '0px', '0px', '0px'])
}
await buttons[1].trigger('click')
expect(buttons[1].classes()).toContain('active')
await buttons[0].trigger('click')
expect(buttons[0].classes()).toContain('active')
}
})
it('allows specific component rules to override theme defaults', () => {
document.documentElement.dataset.theme = theme
document.documentElement.dataset.bsTheme = theme
const override = document.createElement('style')
override.textContent = '.card.custom-card { background-color: rgb(1, 2, 3); color: rgb(4, 5, 6); }'
document.head.append(override)
try {
const style = componentStyle('<div class="card custom-card"></div>', '.card')
expect(style.backgroundColor).toBe('rgb(1, 2, 3)')
expect(style.color).toBe('rgb(4, 5, 6)')
} finally {
override.remove()
}
})
it.each(componentCases)('preserves %s styles', (_name, markup, selector, expected) => {
document.documentElement.dataset.theme = theme
document.documentElement.dataset.bsTheme = theme
const style = componentStyle(markup, selector)
for (const [property, value] of Object.entries(expected)) {
expect(style[property], property).toBe(value)
}
})
it('themes the rendered official and platform badges without Bootstrap background utilities', async () => {
document.documentElement.dataset.theme = theme
document.documentElement.dataset.bsTheme = theme
await mountFeatured({ apps: [{ id: 'sample', name: 'Sample App', official: true, platforms: ['linux'] }] })
const [official, platform] = wrapper.findAll('.badge')
expect(getComputedStyle(official.element).backgroundColor).toBe('var(--color-primary)')
expect(getComputedStyle(official.element).color).toBe('var(--color-on-primary)')
expect(getComputedStyle(platform.element).backgroundColor).toBe('var(--color-bg-muted)')
expect(getComputedStyle(platform.element).color).toBe('var(--color-text-base)')
})
})