Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
14cfdfb
refactor: working on some modal enhancements
nulfrost Jan 31, 2026
2515f33
chore: re-add backdrop when modals are open
nulfrost Jan 31, 2026
bcee3c3
chore: move modal transition styles into main.css
nulfrost Jan 31, 2026
219e454
chore: adjust padding and roundness on connect menu items
nulfrost Jan 31, 2026
09fd230
Merge branch 'main' of github.com:nulfrost/npmx.dev into refactor/mod…
nulfrost Jan 31, 2026
fde38d0
refactor: define ids for header dropdown modals, switch ConnectorModa…
nulfrost Jan 31, 2026
6cad49e
chore: replacing open modal calls with new functions
nulfrost Jan 31, 2026
0b542da
fix: use the :modal attribute for modal styling to fix safari issues
nulfrost Jan 31, 2026
8986ea1
chore: conflicts
nulfrost Jan 31, 2026
9504599
feat: add closedby='any' attribute to dialog to allow for light dismiss
nulfrost Jan 31, 2026
46b32bb
chore: add things back that were removed due to conflict
nulfrost Jan 31, 2026
2204fd3
refactor: use modal component for ChartModal
nulfrost Jan 31, 2026
4d12eb1
refactor: update ClaimPackageModal to use Modal component
nulfrost Jan 31, 2026
a6ccf6a
feat: trying to add light dismiss for safari
nulfrost Jan 31, 2026
906ec24
fix: add setTimeout to allow for the light dismiss workaround to func…
nulfrost Jan 31, 2026
c7fbab0
fix: add the correct type for handleModalLightDismiss
nulfrost Jan 31, 2026
f599c4b
Merge branch 'main' of github.com:nulfrost/npmx.dev into refactor/mod…
nulfrost Jan 31, 2026
8f45120
fix: adjust import for ConnectorModal in component.spec.ts
nulfrost Jan 31, 2026
565c6da
feat: create useModal composable
nulfrost Jan 31, 2026
62515f5
fix: small fixes
danielroe Feb 1, 2026
5e67d2d
fix: small fixes
danielroe Feb 1, 2026
d303dd1
Merge remote-tracking branch 'origin/main' into refactor/modal-a11y
danielroe Feb 1, 2026
4295395
refactor: use useModal
danielroe Feb 1, 2026
8b84f93
fix: add scrollbar-gutter: stable to remove layout shift when modals …
nulfrost Feb 1, 2026
8f56e14
fix: don't navigate backwards when modal is open and close button is …
nulfrost Feb 1, 2026
4fcecdf
fix: use .matches() to stop navigation when the modal is open on the …
nulfrost Feb 1, 2026
3bdff82
Merge branch 'main' of github.com:nulfrost/npmx.dev into refactor/mod…
nulfrost Feb 1, 2026
4066568
Merge branch 'main' of github.com:nulfrost/npmx.dev into refactor/mod…
nulfrost Feb 1, 2026
652a076
Merge remote-tracking branch 'origin/main' into refactor/modal-a11y
danielroe Feb 1, 2026
118ff4b
fix: ensure graph uses up-to-date data
danielroe Feb 1, 2026
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
11 changes: 11 additions & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,20 @@ function handleGlobalKeyup() {
showKbdHints.value = false
}

/* A hack to get light dismiss to work in safari because it does not support closedby="any" yet */
// https://codepen.io/paramagicdev/pen/gbYompq
// see: https://github.com/npmx-dev/npmx.dev/pull/522#discussion_r2749978022
function handleModalLightDismiss(e: MouseEvent) {
const target = e.target as HTMLElement
if (target.tagName === 'DIALOG' && target.hasAttribute('open')) {
;(target as HTMLDialogElement).close()
}
}

Comment thread
nulfrost marked this conversation as resolved.
if (import.meta.client) {
useEventListener(document, 'keydown', handleGlobalKeydown)
useEventListener(document, 'keyup', handleGlobalKeyup)
useEventListener(document, 'click', handleModalLightDismiss)
}
</script>

Expand Down
6 changes: 6 additions & 0 deletions app/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,9 @@ input[type='search']::-webkit-search-results-decoration {
animation-duration: 0.3s;
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Locking the scroll whenever any of the modals are open */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works reliably to scroll lock including Safari macOS and iOS:

html:has(dialog:modal),
html:has(dialog:modal) body {
	overflow: hidden;
  height: 100%;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm no difference on my end

Screen.Recording.2026-01-31.at.11.29.23.PM.mov

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I seemed to have a cached version on my iPhone—seems to work fine on the preview.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it seems to be inconsistent. Like the first time you open it, it’s fine, but then you do something else and open it again and suddenly the scroll isn’t locked. In any case, that seems like an edge case and I think we can ignore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the explicit scrollbar width seemed to do it, wonder if there's a way to keep it and have no shift

html:has(dialog:modal) {
Copy link
Copy Markdown
Member

@knowler knowler Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the effect of this: when the modal opens the scrollbar for the page disappears which causes a bit of a shift. It might be worth adding scrollbar-gutter: stable to avoid that (might need to add it to html without the :has(dialog:modal)).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, good call

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very odd, it seems like Firefox is working exactly as expected, but I’m not seeing it work in Safari or Chrome.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh weird, I'm seeing it work in Firefox and Chrome but not Safari.. 🤔

overflow: hidden;
scrollbar-gutter: stable;
}
144 changes: 144 additions & 0 deletions app/components/AuthModal.client.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script setup lang="ts">
const handleInput = shallowRef('')

const { user, logout } = useAtproto()

async function handleBlueskySignIn() {
await navigateTo(
{
path: '/api/auth/atproto',
query: { handle: 'https://bsky.social' },
},
{ external: true },
)
}

async function handleCreateAccount() {
await navigateTo(
{
path: '/api/auth/atproto',
query: { handle: 'https://npmx.social', create: 'true' },
},
{ external: true },
)
}

async function handleLogin() {
if (handleInput.value) {
await navigateTo(
{
path: '/api/auth/atproto',
query: { handle: handleInput.value },
},
{ external: true },
)
}
}
</script>

<template>
<!-- Modal -->
<Modal :modalTitle="$t('auth.modal.title')" class="max-w-lg" id="auth-modal">
<div v-if="user?.handle" class="space-y-4">
<div class="flex items-center gap-3 p-4 bg-bg-subtle border border-border rounded-lg">
<span class="w-3 h-3 rounded-full bg-green-500" aria-hidden="true" />
<div>
<p class="font-mono text-xs text-fg-muted">
{{ $t('auth.modal.connected_as', { handle: user.handle }) }}
</p>
</div>
</div>
<button
class="w-full px-4 py-2 font-mono text-sm text-fg-muted bg-bg-subtle border border-border rounded-md transition-colors duration-200 hover:text-fg hover:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
@click="logout"
>
{{ $t('auth.modal.disconnect') }}
</button>
</div>

<!-- Disconnected state -->
<form v-else class="space-y-4" @submit.prevent="handleLogin">
<p class="text-sm text-fg-muted">{{ $t('auth.modal.connect_prompt') }}</p>

<div class="space-y-3">
<div>
<label
for="handle-input"
class="block text-xs text-fg-subtle uppercase tracking-wider mb-1.5"
>
{{ $t('auth.modal.handle_label') }}
</label>
<input
id="handle-input"
v-model="handleInput"
type="text"
name="handle"
:placeholder="$t('auth.modal.handle_placeholder')"
autocomplete="off"
spellcheck="false"
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
/>
</div>

<details class="text-sm">
<summary
class="text-fg-subtle cursor-pointer hover:text-fg-muted transition-colors duration-200"
>
{{ $t('auth.modal.what_is_atmosphere') }}
</summary>
<div class="mt-3">
<i18n-t keypath="auth.modal.atmosphere_explanation" tag="p">
<template #npmx>
<span class="font-bold">npmx.dev</span>
</template>
<template #atproto>
<a href="https://atproto.com" target="_blank" class="text-blue-400 hover:underline">
AT Protocol
</a>
</template>
<template #bluesky>
<a href="https://bsky.app" target="_blank" class="text-blue-400 hover:underline">
Bluesky
</a>
</template>
<template #tangled>
<a href="https://tangled.org" target="_blank" class="text-blue-400 hover:underline">
Tangled
</a>
</template>
</i18n-t>
</div>
</details>
</div>

<button
type="submit"
:disabled="!handleInput.trim()"
class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
>
{{ $t('auth.modal.connect') }}
</button>
<button
type="button"
class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
@click="handleCreateAccount"
>
{{ $t('auth.modal.create_account') }}
</button>
<hr />
<button
type="button"
class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg flex items-center justify-center gap-2"
@click="handleBlueskySignIn"
>
{{ $t('auth.modal.connect_bluesky') }}
<svg fill="none" viewBox="0 0 64 57" width="20" style="width: 20px">
<path
fill="#0F73FF"
d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z"
></path>
</svg>
</button>
</form>
</Modal>
</template>
198 changes: 0 additions & 198 deletions app/components/AuthModal.vue

This file was deleted.

Loading
Loading