Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions 20 app/components/ColorScheme/Img.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
const props = defineProps<{
lightSrc: string
darkSrc: string
}>()
Comment thread
alexdln marked this conversation as resolved.
</script>

<template>
<img
:src="props.darkSrc"
class="color-mode-img"
:style="`--light-src: url('${props.lightSrc}')`"
/>
</template>

<style>
.light .color-mode-img {
content: var(--light-src);
}
</style>
138 changes: 138 additions & 0 deletions 138 app/components/Landing/IntroHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<script setup lang="ts">
import { ACTIVE_NOODLES, PERMANENT_NOODLES, type Noodle } from '../Noodle'

const { env } = useAppConfig().buildInfo

const activeNoodlesData = ACTIVE_NOODLES.map(noodle => ({
key: noodle.key,
date: noodle.date,
timezone: noodle.timezone,
tagline: noodle.tagline,
}))

const permanentNoodlesData = PERMANENT_NOODLES.map(noodle => ({
key: noodle.key,
tagline: noodle.tagline,
}))

onPrehydrate(el => {
const tagline = el.querySelector<HTMLElement>('#intro-header-tagline')
const defaultLogo = el.querySelector<HTMLElement>('#intro-header-noodle-default')

if (!tagline || !defaultLogo) return

let permanentNoodles
try {
permanentNoodles = JSON.parse(el.dataset.permanentNoodles as string) as Noodle[]
} catch {
return
}
const activePermanentNoodle = permanentNoodles?.find(noodle =>
new URLSearchParams(window.location.search).has(noodle.key),
)

if (activePermanentNoodle) {
const permanentNoodleLogo = el.querySelector<HTMLElement>(
`#intro-header-noodle-${activePermanentNoodle.key}`,
)

if (!permanentNoodleLogo) return

permanentNoodleLogo.style.display = 'block'
defaultLogo.style.display = 'none'
if (activePermanentNoodle.tagline === false) {
tagline.style.display = 'none'
}
return
}

let activeNoodles
try {
activeNoodles = JSON.parse(el.dataset.activeNoodles as string) as Noodle[]
} catch {
return
}

const currentActiveNoodles = activeNoodles.filter(noodle => {
const todayDate = new Intl.DateTimeFormat('en-US', {
timeZone: noodle.timezone === 'auto' ? undefined : noodle.timezone,
month: '2-digit',
day: '2-digit',
year: 'numeric',
}).format(new Date())

const noodleDate =
noodle.date &&
new Intl.DateTimeFormat('en-US', {
timeZone: noodle.timezone === 'auto' ? undefined : noodle.timezone,
month: '2-digit',
day: '2-digit',
year: 'numeric',
}).format(new Date(noodle.date))
return todayDate === noodleDate
})

if (!currentActiveNoodles.length) return

const roll = Math.floor(Math.random() * currentActiveNoodles.length)
const selectedNoodle = currentActiveNoodles[roll]

if (!selectedNoodle) return

const noodleLogo = el.querySelector<HTMLElement>(`#intro-header-noodle-${selectedNoodle.key}`)

if (!defaultLogo || !noodleLogo || !tagline) return

defaultLogo.style.display = 'none'
noodleLogo.style.display = 'block'
if (selectedNoodle.tagline === false) {
tagline.style.display = 'none'
}
})
</script>

<template>
<div
:data-active-noodles="JSON.stringify(activeNoodlesData)"
:data-permanent-noodles="JSON.stringify(permanentNoodlesData)"
>
<h1 class="sr-only">
{{ $t('alt_logo') }}
</h1>
<div
id="intro-header-noodle-default"
class="relative mb-6 w-fit mx-auto motion-safe:animate-fade-in motion-safe:animate-fill-both"
aria-hidden="true"
>
<AppLogo id="npmx-index-h1-logo-normal" class="w-42 h-auto sm:w-58 md:w-70" />
<span
id="npmx-index-h1-logo-env"
class="text-sm sm:text-base md:text-lg transform-origin-br font-mono tracking-widest text-accent absolute -bottom-4 -inset-ie-1.5"
>
{{ env === 'release' ? 'alpha' : env }}
</span>
</div>
<component
v-for="noodle in PERMANENT_NOODLES"
:key="noodle.key"
:id="`intro-header-noodle-${noodle.key}`"
class="hidden"
aria-hidden="true"
:is="noodle.logo"
/>
<component
v-for="noodle in ACTIVE_NOODLES"
:key="noodle.key"
:id="`intro-header-noodle-${noodle.key}`"
class="hidden"
aria-hidden="true"
:is="noodle.logo"
/>
<p
id="intro-header-tagline"
class="text-fg-muted text-lg sm:text-xl max-w-xl mb-12 lg:mb-14 motion-safe:animate-slide-up motion-safe:animate-fill-both delay-100"
>
{{ $t('tagline') }}
</p>
</div>
</template>
76 changes: 0 additions & 76 deletions 76 app/components/LandingLogo.vue

This file was deleted.

19 changes: 19 additions & 0 deletions 19 app/components/Noodle/Artemis/Logo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div>
<ColorSchemeImg
width="400"
class="mb-8 motion-safe:animate-fade-in motion-safe:animate-scale-in w-80 sm:w-100"
dark-src="/extra/npmx-dark-artemis.svg"
light-src="/extra/npmx-light-artemis.svg"
alt=""
/>
<ColorSchemeImg
width="1440"
height="455"
class="absolute bottom-0 inset-is-0 w-full h-auto mix-blend-lighten light:mix-blend-darken motion-safe:animate-fade-in"
dark-src="/extra/moon-dark.png"
light-src="/extra/moon-light.png"
alt=""
/>
Comment thread
alexdln marked this conversation as resolved.
</div>
</template>
8 changes: 8 additions & 0 deletions 8 app/components/Noodle/Kawaii/Logo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<img
width="400"
class="mb-8 motion-safe:animate-fade-in motion-safe:animate-scale-in motion-safe:hover:scale-105 motion-safe:transition w-80 sm:w-100"
src="/extra/npmx-cute.svg"
:alt="$t('alt_logo_kawaii')"
/>
</template>
47 changes: 47 additions & 0 deletions 47 app/components/Noodle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import NoodleKawaiiLogo from './Kawaii/Logo.vue'
import NoodleArtemisLogo from './Artemis/Logo.vue'
// import NoodleTkawaiiLogo from './Tkawaii/Logo.vue'

export type Noodle = {
// Unique identifier for the noodle
key: string
// Timezone for the noodle (default is auto, i.e. user's timezone)
timezone?: string
// Date for the noodle
date?: string
// Logo for the noodle - could be any component. Relative parent - intro section
logo: Component
// Show npmx tagline or not (default is true)
tagline?: boolean
}

// Archive noodles - might be shown on special page
// export const ARCHIVE_NOODLES: Noodle[] = [
// {
// key: 'tkawaii',
// date: '2026-04-08T12:00:00UTC',
// timezone: 'auto',
// logo: NoodleTkawaiiLogo,
// tagline: false,
// },
// ]

// Permanent noodles - always shown on specific query param (e.g. ?kawaii)
export const PERMANENT_NOODLES: Noodle[] = [
{
key: 'kawaii',
logo: NoodleKawaiiLogo,
tagline: false,
},
]

// Active noodles - shown based on date and timezone
export const ACTIVE_NOODLES: Noodle[] = [
{
key: 'artemis',
logo: NoodleArtemisLogo,
date: '2026-04-08T12:00:00Z',
timezone: 'America/Los_Angeles',
tagline: true,
},
]
4 changes: 2 additions & 2 deletions 4 app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ defineOgImageComponent('Default', {

<template>
<main>
<section class="container min-h-screen flex flex-col">
<section class="relative container min-h-screen flex flex-col overflow-hidden">
<header
class="flex-1 flex flex-col items-center justify-center text-center pt-20 pb-4 md:pb-8 lg:pb-20"
>
<LandingLogo class="w-42 h-auto sm:w-58 md:w-70" />
<LandingIntroHeader />
<search
class="w-full max-w-2xl motion-safe:animate-slide-up motion-safe:animate-fill-both"
style="animation-delay: 0.2s"
Expand Down
Binary file added BIN +553 KB public/extra/moon-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added BIN +567 KB public/extra/moon-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions 20 public/extra/npmx-dark-artemis.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions 9 public/extra/npmx-light-artemis.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 19 additions & 3 deletions 22 test/nuxt/a11y.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ import {
BlueskyPostEmbed,
BuildEnvironment,
ButtonBase,
LandingLogo,
LandingIntroHeader,
NoodleKawaiiLogo,
NoodleArtemisLogo,
LinkBase,
CallToAction,
CodeDirectoryListing,
Expand Down Expand Up @@ -354,9 +356,23 @@ describe('component accessibility audits', () => {
})
})

describe('LandingLogo', () => {
describe('LandingIntroHeader', () => {
it('should have no accessibility violations', async () => {
const component = await mountSuspended(LandingLogo)
const component = await mountSuspended(LandingIntroHeader)
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
})

describe('Noodles', () => {
it('should have no accessibility violations', async () => {
const component = await mountSuspended(NoodleKawaiiLogo)
const results = await runAxe(component)
expect(results.violations).toEqual([])
})

it('should have no accessibility violations', async () => {
const component = await mountSuspended(NoodleArtemisLogo)
const results = await runAxe(component)
expect(results.violations).toEqual([])
})
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.