Misskey Login/Welcome Page Layout#
Overview#
The welcome/login page (/ for unauthenticated visitors) is composed of layered Vue components with carefully separated scroll contexts. The entry point is welcome.vue, which renders one of three sub-pages depending on instance state:
XSetup— first-run setup flowXEntranceClassic— the default "classic" style (controlled byinstance.clientOptions.entrancePageStyle)XEntranceSimple— an alternate simpler layout
Classic Layout: Component Layers (welcome.entrance.classic.vue)#
The classic entrance assembles several independently positioned layers inside a single .root container:
| Element | Positioning | Purpose |
|---|---|---|
MkFeaturedPhotos (.bg) | position: fixed, width: 80vw, height: 100vh | Right-side photo collage background |
XTimeline (.tl) | position: fixed, right-aligned, hidden below 1200px | Auto-scrolling featured posts panel |
.shape1 / .shape2 | position: fixed, full-viewport | Accent-colored polygonal decorative shapes (left side) |
.logoWrapper | position: fixed, top: 36px; left: 36px | "Powered by Misskey" branding |
.contents | position: relative | Scrollable content area wrapping MkVisitorDashboard |
.federation | position: fixed, bottom: 16px | Marquee of federated instances |
All decorative layers (background, shapes, logo, federation bar) are fixed, so they stay in place while only .contents scrolls .
Scroll Management#
Root container scroll context#
The .root element in both classic and simple entrances uses :
height: 100cqh;
overflow: auto;
overscroll-behavior: contain;
100cqh— sizes the root to the container's query height, not the viewport, to avoid iOS Safari bounce affecting the full page.overflow: auto— creates a self-contained scroll context; the fixed-positioned children anchor to the viewport within this scroll container.overscroll-behavior: contain— prevents scroll chaining to the outer document so overscrolling the welcome page doesn't trigger browser navigation gestures or bounce effects.
Global body/html scroll containment#
At the global level, html, body, #misskey_app all have overscroll-behavior: none and body has overflow: clip . This means no scroll can escape to the document root — the welcome page's .root is the sole scroll surface.
._pageContainer and ._pageScrollable utility classes#
For other pages in the app, style.scss defines reusable scroll primitives :
._pageContainer:overflow: auto; overscroll-behavior: containwithcontainer-type: size— this is what provides thecqhunit used by the welcome root.._pageScrollable:overflow-y: scroll; overscroll-behavior: contain; background: var(--MI_THEME-bg)— the background is required to keep this container on the GPU compositor thread in Chrome (as noted in the source comments).
Visitor Dashboard (MkVisitorDashboard.vue)#
MkVisitorDashboard renders inside .contents and provides:
- Instance icon, name, description, registration warnings
- "Join this server," "Explore other servers," and "Login" action buttons
- Optional stats grid (users/notes counts) and a local timeline preview
The login button calls os.popup(XSigninDialog, ...) which opens MkSigninDialog — a modal dialog layered on top of the welcome page.
Sign-in Dialog (MkSigninDialog.vue)#
MkSigninDialog wraps MkSignin inside MkModal and is sized as a fixed-center dialog :
- Max
400px × 450px,overflow: auto - Sticky header with blur backdrop (
position: sticky; top: 0)
Timeline Panel (welcome.timeline.vue)#
The right-side scrolling timeline is purely decorative and uses CSS @keyframes animations (scrollIntro → scrollConstant) driven by transform: translate3d for GPU acceleration. It pauses on hover via :has(.note:hover). Hidden below 1200px viewport width via media query .
Key Files#
| File | Role |
|---|---|
pages/welcome.vue | Entry point — selects entrance variant |
pages/welcome.entrance.classic.vue | Classic layout, all layer CSS |
components/MkVisitorDashboard.vue | Login/signup action panel |
components/MkSigninDialog.vue | Sign-in modal dialog |
src/style.scss | Global scroll, ._pageContainer, ._pageScrollable |