+
-
+
diff --git a/app/pages/~[username]/orgs.vue b/app/pages/~[username]/orgs.vue
index a88dbdc6e..06c7f6ad2 100644
--- a/app/pages/~[username]/orgs.vue
+++ b/app/pages/~[username]/orgs.vue
@@ -81,6 +81,8 @@ async function loadOrgs() {
}
}
+error.value = $t('header.orgs_dropdown.error')
+
// Load on mount and when connection status changes
watch(isOwnProfile, loadOrgs, { immediate: true })
@@ -158,10 +160,11 @@ defineOgImageComponent('Default', {
{{ $t('user.orgs_page.own_orgs_only') }}
-
{{ $t('user.orgs_page.view_your_orgs') }}{{ $t('user.orgs_page.view_your_orgs') }}
@@ -171,7 +174,7 @@ defineOgImageComponent('Default', {
{{ error }}
-
+
{{ $t('common.try_again') }}
diff --git a/test/nuxt/a11y.spec.ts b/test/nuxt/a11y.spec.ts
index ad07e9664..7a171073b 100644
--- a/test/nuxt/a11y.spec.ts
+++ b/test/nuxt/a11y.spec.ts
@@ -81,6 +81,8 @@ import {
AppLogo,
BaseCard,
BuildEnvironment,
+ ButtonBase,
+ LinkBase,
CallToAction,
CodeDirectoryListing,
CodeFileTree,
@@ -140,8 +142,6 @@ import {
SettingsBgThemePicker,
SettingsToggle,
TagStatic,
- TagButton,
- TagLink,
TagRadioButton,
TerminalExecute,
TerminalInstall,
@@ -290,57 +290,98 @@ describe('component accessibility audits', () => {
})
})
- describe('TagButton', () => {
+ describe('ButtonBase', () => {
it('should have no accessibility violations', async () => {
- const component = await mountSuspended(TagButton, {
- slots: { default: 'Tag content' },
+ const component = await mountSuspended(ButtonBase, {
+ slots: { default: 'Button content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
- it('should have no accessibility violations when pressed', async () => {
- const component = await mountSuspended(TagButton, {
- props: { pressed: true },
- slots: { default: 'Tag content' },
+ it('should have no accessibility violations for disabled state', async () => {
+ const component = await mountSuspended(ButtonBase, {
+ props: { disabled: true },
+ slots: { default: 'Button content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
- it('should have no accessibility violations for disabled state', async () => {
- const component = await mountSuspended(TagButton, {
- props: { disabled: true },
- slots: { default: 'Tag content' },
+ it('should have no accessibility violations as primary button', async () => {
+ const component = await mountSuspended(ButtonBase, {
+ props: { variant: 'primary' },
+ slots: { default: 'Button content' },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations with size small', async () => {
+ const component = await mountSuspended(ButtonBase, {
+ props: { size: 'small' },
+ slots: { default: 'Button content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
})
- describe('TagLink', () => {
+ describe('LinkBase', () => {
it('should have no accessibility violations', async () => {
- const component = await mountSuspended(TagLink, {
- props: { href: 'http://example.com' },
- slots: { default: 'Tag content' },
+ const component = await mountSuspended(LinkBase, {
+ props: { to: 'http://example.com' },
+ slots: { default: 'Button link content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
it("should have no accessibility violations when it's the current link", async () => {
- const component = await mountSuspended(TagLink, {
- props: { href: 'http://example.com', current: true },
- slots: { default: 'Tag content' },
+ const component = await mountSuspended(LinkBase, {
+ props: { to: 'http://example.com', current: true },
+ slots: { default: 'Button link content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
it('should have no accessibility violations when disabled (plain text)', async () => {
- const component = await mountSuspended(TagLink, {
- props: { href: 'http://example.com', disabled: true },
- slots: { default: 'Tag content' },
+ const component = await mountSuspended(LinkBase, {
+ props: { to: 'http://example.com', disabled: true },
+ slots: { default: 'Button link content' },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations as secondary button', async () => {
+ const component = await mountSuspended(LinkBase, {
+ props: { to: 'http://example.com', disabled: true, variant: 'button-secondary' },
+ slots: { default: 'Button link content' },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations as primary button', async () => {
+ const component = await mountSuspended(LinkBase, {
+ props: { to: 'http://example.com', disabled: true, variant: 'button-primary' },
+ slots: { default: 'Button link content' },
+ })
+ const results = await runAxe(component)
+ expect(results.violations).toEqual([])
+ })
+
+ it('should have no accessibility violations as small button', async () => {
+ const component = await mountSuspended(LinkBase, {
+ props: {
+ to: 'http://example.com',
+ disabled: true,
+ variant: 'button-secondary',
+ size: 'small',
+ },
+ slots: { default: 'Button link content' },
})
const results = await runAxe(component)
expect(results.violations).toEqual([])
diff --git a/test/unit/a11y-component-coverage.spec.ts b/test/unit/a11y-component-coverage.spec.ts
index 42502bc41..0cc0e40e5 100644
--- a/test/unit/a11y-component-coverage.spec.ts
+++ b/test/unit/a11y-component-coverage.spec.ts
@@ -43,6 +43,7 @@ const SKIPPED_COMPONENTS: Record
= {
'UserCombobox.vue': 'Unused component - intended for future admin features',
'SkeletonBlock.vue': 'Already covered indirectly via other component tests',
'SkeletonInline.vue': 'Already covered indirectly via other component tests',
+ 'Button/Group.vue': "Wrapper component, tests wouldn't make much sense here",
}
/**
diff --git a/uno.config.ts b/uno.config.ts
index 0dadcae35..a7a529e72 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -126,21 +126,6 @@ export default defineConfig({
// Focus states - subtle but accessible
['focus-ring', 'outline-none focus-visible:(ring-2 ring-fg/50 ring-offset-2)'],
- // Buttons
- [
- 'btn',
- 'inline-flex items-center justify-center px-4 py-2 font-mono text-sm border border-border rounded-md bg-transparent text-fg transition-all duration-200 hover:(bg-fg hover:text-bg border-fg) focus-ring active:scale-98 disabled:(opacity-40 cursor-not-allowed hover:bg-transparent hover:text-fg)',
- ],
- [
- 'btn-ghost',
- 'inline-flex items-center justify-center px-3 py-1.5 font-mono text-sm text-fg-muted bg-transparent transition-all duration-200 hover:text-fg focus-ring',
- ],
-
- // Links
- [
- 'link',
- 'text-fg underline-offset-4 decoration-border hover:(decoration-fg underline) transition-colors duration-200 focus-ring',
- ],
['link-subtle', 'text-fg-muted hover:text-fg transition-colors duration-200 focus-ring'],
// badges