AI 与对话 · 助手与对话 · 公开
AI Assistant App
“AI Assistant App”|来源 MotionSites|原分类 应用
#应用#MotionSites
Prompt 原文27,230 字符
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#303238">
<title>Nixito — Screen Gallery (Standalone)</title>
<style>
@font-face {
font-family: "Mont Screen 3";
src: url("assets/home/fonts/Mont-Screen3-Regular.woff2") format("woff2"),
url("assets/home/fonts/Mont-Screen3-Regular.ttf") format("truetype");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "Mont Screen 3";
src: url("assets/home/fonts/Mont-Screen3-SemiBold.woff2") format("woff2"),
url("assets/home/fonts/Mont-Screen3-SemiBold.ttf") format("truetype");
font-style: normal;
font-weight: 600;
font-display: swap;
}
@font-face {
font-family: "Mont Screen 3";
src: url("assets/home/fonts/Mont-Screen3-Bold.woff2") format("woff2"),
url("assets/home/fonts/Mont-Screen3-Bold.ttf") format("truetype");
font-style: normal;
font-weight: 700;
font-display: swap;
}
:root {
color-scheme: dark;
--page: #08090d;
--stage: #20232a;
--stage-border: rgba(255, 255, 255, .13);
--chrome: #242424;
--muted: #a8a8a8;
--screen-width: 390px;
--screen-height: 844px;
--rendered-screen-width: 390px;
--rendered-screen-height: 844px;
--screen-scale: 1;
--stage-padding-y: clamp(20px, 4vh, 48px);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
html {
min-width: 320px;
min-height: 100%;
background: #303238;
}
body {
width: 100%;
height: 100vh;
height: 100dvh;
margin: 0;
overflow: hidden;
background: #303238;
color: #fff;
}
main {
width: 100%;
height: 100%;
}
.screen-gallery {
width: 100%;
height: 100vh;
height: 100dvh;
margin: 0;
overflow-x: auto;
overflow-y: hidden;
border: 0;
background: #303238;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .07),
inset 0 -1px 0 rgba(0, 0, 0, .24);
scrollbar-color: #9299a5 #15181e;
scrollbar-gutter: stable;
scrollbar-width: auto;
scroll-behavior: smooth;
scroll-snap-type: x proximity;
overscroll-behavior-inline: contain;
}
.screen-gallery.is-dragging {
cursor: grabbing;
scroll-behavior: auto;
user-select: none;
}
.screen-gallery:focus-visible {
outline: 2px solid #fff;
outline-offset: 3px;
}
.screen-gallery::-webkit-scrollbar { height: 15px; }
.screen-gallery::-webkit-scrollbar-track { background: #15181e; }
.screen-gallery::-webkit-scrollbar-thumb {
border: 4px solid #15181e;
border-radius: 999px;
background: #9299a5;
}
.screen-gallery::-webkit-scrollbar-thumb:hover { background: #c2c7cf; }
.screen-track {
display: flex;
width: max-content;
min-width: 100%;
height: 100%;
align-items: center;
gap: clamp(32px, 2.5vw, 52px);
padding: var(--stage-padding-y) clamp(42px, 3.25vw, 68px);
cursor: grab;
}
.screen-card { cursor: auto; }
.screen-card {
position: relative;
width: var(--rendered-screen-width);
height: var(--rendered-screen-height);
flex: 0 0 var(--rendered-screen-width);
margin: 0;
overflow: hidden;
background: #fff;
box-shadow:
0 1px 0 rgba(255, 255, 255, .1),
0 22px 54px rgba(0, 0, 0, .28);
scroll-snap-align: center;
scroll-snap-stop: normal;
}
screen-preview {
display: block;
width: var(--screen-width);
height: var(--screen-height);
background: #fff;
transform: scale(var(--screen-scale));
transform-origin: top left;
}
@media (max-width: 640px) {
.screen-gallery {
width: 100%;
height: 100vh;
height: 100dvh;
}
.screen-track { gap: 24px; padding-right: 20px; padding-left: 20px; }
}
@media (prefers-reduced-motion: reduce) {
.screen-gallery { scroll-behavior: auto; }
}
</style>
</head>
<body>
<main>
<section id="screen-gallery" class="screen-gallery" aria-label="Nixito mobile screen gallery" tabindex="0">
<div class="screen-track">
<!-- SCREEN 1: Login -->
<article class="screen-card" aria-label="Login screen">
<screen-preview data-screen="login">
<template>
<style>
@font-face {
font-family: "Mont";
src: url("assets/login/fonts/Mont-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Mont";
src: url("assets/login/fonts/Mont-SemiBold.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
:host { display: block; width: 390px; height: 844px; overflow: hidden; contain: strict; background: #fff; }
.screen-body { width: 390px; height: 844px; min-width: 390px; min-height: 844px; overflow: hidden; }
:host, :root {
--page: #ffffff;
--ink: #000000;
--muted: rgba(77, 77, 77, 0.6);
--border: #dfd5c9;
--pill-radius: 100px;
--card-radius: 16px;
}
* { box-sizing: border-box; }
:host, .screen-body { min-width: 100%; min-height: 100%; margin: 0; }
.screen-body {
display: grid;
place-items: start center;
overflow-x: auto;
background: #ececec;
color: var(--ink);
font-family: "Mont", Arial, sans-serif;
}
button, input { font: inherit; }
.app-screen {
position: relative;
width: 390px;
height: 844px;
flex: 0 0 390px;
overflow: hidden;
background: var(--page) url("assets/login/images/login-background.png") center / cover no-repeat;
}
.login-panel {
position: absolute;
top: 100px;
left: 40px;
z-index: 2;
display: flex;
width: 310px;
height: 400px;
flex-direction: column;
align-items: center;
gap: 30px;
}
h1 {
width: 275px;
height: 72px;
margin: 0;
font-size: 32px;
font-weight: 600;
line-height: 36px;
text-align: center;
}
.form-fields {
display: flex;
width: 310px;
height: 210px;
flex-direction: column;
gap: 16px;
}
.field-group {
display: flex;
width: 310px;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.field-group label {
height: 20px;
padding-left: 12px;
color: var(--muted);
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
.field-shell {
display: flex;
width: 310px;
height: 54px;
align-items: center;
padding: 16px 28px 16px 24px;
border: 1px solid var(--border);
border-radius: 30px;
}
.field-shell input {
width: 100%;
margin: 0;
padding: 0;
border: 0;
outline: 0;
background: transparent;
color: var(--ink);
font-size: 16px;
font-weight: 400;
line-height: 22px;
pointer-events: none;
}
.password-section {
display: flex;
width: 310px;
height: 116px;
flex-direction: column;
align-items: flex-end;
gap: 16px;
}
.password-group { height: 80px; }
.password-shell { height: 56px; justify-content: space-between; }
.password-shell input { width: 175px; padding-top: 5px; font-weight: 400; letter-spacing: 0; }
.visibility-icon { flex: 0 0 auto; color: var(--ink); }
.forgot-password {
width: 127px;
height: 20px;
margin: 0;
color: var(--muted);
font-size: 14px;
font-weight: 400;
line-height: 20px;
white-space: nowrap;
}
.login-button {
display: flex;
width: 310px;
height: 58px;
align-items: center;
justify-content: center;
padding: 17px 0;
border: 0;
border-radius: var(--pill-radius);
background: var(--ink);
color: #ffffff;
font-size: 20px;
font-weight: 600;
line-height: 24px;
}
.robot-gallery {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
}
.robot-card {
position: absolute;
width: 80px;
height: 110px;
overflow: hidden;
border-radius: var(--card-radius);
background: #d9d9d9;
transform-origin: center;
}
.robot-card img {
position: absolute;
display: block;
max-width: none;
object-fit: fill;
transform-origin: 0 0;
}
.card-ant { top: 534.89px; left: -44.94px; transform: rotate(10deg); }
.card-ant img { top: -10.1573px; left: -21.1976px; width: 153.3427px; height: 114.802px; transform: rotate(5deg); }
.card-orange { top: 567.72px; left: 55.36px; transform: rotate(5deg); }
.card-orange img { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 29%; transform: none; }
.card-tractor { top: 601px; left: 156px; }
.card-tractor img { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 51% center; transform: none; }
.card-runner { top: 567.72px; left: 247.36px; transform: rotate(-5deg); }
.card-runner img { top: -36px; left: -67px; width: 269px; height: 201px; }
.card-koala { top: 534.89px; left: 339.06px; transform: rotate(-10deg); }
.card-koala img { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% center; transform: none; }
.signup-copy {
position: absolute;
top: 784px;
left: 118px;
z-index: 2;
width: 155px;
height: 20px;
margin: 0;
color: #4d4d4d;
font-size: 14px;
font-weight: 400;
line-height: 20px;
white-space: nowrap;
}
.signup-copy span {
color: var(--ink);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
}
</style>
<div class="screen-body">
<main class="app-screen" aria-label="Rovio login screen">
<section class="login-panel">
<h1>Welcome Back<br>to Rovio</h1>
<div class="form-fields">
<div class="field-group">
<label for="email">E-mail</label>
<div class="field-shell">
<input id="email" type="text" value="hello.nixtio@gmail.com" readonly tabindex="-1">
</div>
</div>
<div class="password-section">
<div class="field-group password-group">
<label for="password">Password</label>
<div class="field-shell password-shell">
<input id="password" type="text" value="************" readonly tabindex="-1">
<svg class="visibility-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M19.439 15.439C20.3636 14.5212 21.0775 13.6091 21.544 12.955C21.848 12.5287 22 12.3155 22 12C22 11.6845 21.848 11.4713 21.544 11.045C20.1779 9.12944 16.6892 5 12 5C11.0922 5 10.2294 5.15476 9.41827 5.41827M6.74742 6.74742C4.73118 8.1072 3.24215 9.94266 2.45604 11.045C2.15201 11.4713 2 11.6845 2 12C2 12.3155 2.15201 12.5287 2.45604 12.955C3.8221 14.8706 7.31078 19 12 19C13.9908 19 15.7651 18.2557 17.2526 17.2526" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.85786 10C9.32783 10.53 9 11.2623 9 12.0711C9 13.6887 10.3113 15 11.9289 15C12.7377 15 13.47 14.6722 14 14.1421" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M3 3L21 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
<p class="forgot-password">Forgot password?</p>
</div>
</div>
<button class="login-button" type="button">Log in</button>
</section>
<section class="robot-gallery" aria-label="Robot artwork">
<div class="robot-card card-ant"><img src="https://flick-award-65707097.figma.site/_assets/v11/3d4592287a68a77e1f39468956467f77b99e0be9.png?w=1024" alt="White ant robot"></div>
<div class="robot-card card-orange"><img src="https://flick-award-65707097.figma.site/_assets/v11/d74df0660205f97a394bf111698bfbc3bf9a6d02.png?h=1536" alt="White and orange robot"></div>
<div class="robot-card card-tractor"><img src="https://flick-award-65707097.figma.site/_assets/v11/692faeeb8862706819c2a2488880f6f2766f051f.png?w=1024" alt="Red robot tractor"></div>
<div class="robot-card card-runner"><img src="https://flick-award-65707097.figma.site/_assets/v11/a042994ae16f2819fe2e45d88c2afafd1fa9c526.png?w=1024" alt="Running white robot"></div>
<div class="robot-card card-koala"><img src="https://flick-award-65707097.figma.site/_assets/v11/998fc400e7936fff51f0830a447fd86bdead2ced.png?w=1024" alt="White koala robot"></div>
</section>
<p class="signup-copy">New to Rovio? <span>Sign up</span></p>
</main>
</div>
</template>
</screen-preview>
</article>
<!-- SCREEN 2: Start / Onboarding -->
<article class="screen-card" aria-label="Start screen">
<screen-preview data-screen="start">
<template>
<style>
@font-face {
font-family: "Mont";
src: url("assets/start/fonts/Mont-Regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "Mont";
src: url("assets/login/fonts/Mont-SemiBold.woff2") format("woff2");
font-style: normal;
font-weight: 600;
font-display: swap;
}
@font-face {
font-family: "Mont";
src: url("assets/start/fonts/Mont-Bold.woff2") format("woff2");
font-style: normal;
font-weight: 700;
font-display: swap;
}
:host { display: block; width: 390px; height: 844px; overflow: hidden; contain: strict; background: #fff; }
.screen-body { width: 390px; height: 844px; min-width: 390px; min-height: 844px; overflow: hidden; }
:host, :root {
color: #050505;
font-family: Mont, "Arial Rounded MT Bold", Arial, sans-serif;
}
* { box-sizing: border-box; }
.screen-body { min-width: 390px; min-height: 100vh; margin: 0; background: #151515; }
.onboarding {
position: relative;
isolation: isolate;
width: 390px;
height: 844px;
margin: auto;
overflow: visible;
background: #babfba;
}
.robot {
position: absolute;
z-index: 1;
top: 298px;
left: 0;
width: 390px;
height: 546px;
object-fit: fill;
display: block;
}
.progress {
display: flex;
position: absolute;
z-index: 2;
top: 50px;
left: 35px;
gap: 6px;
align-items: center;
height: 4px;
width: 321px;
}
.progress span {
display: block;
width: 103px;
height: 4px;
border-radius: 20px;
background: rgba(0, 0, 0, .2);
}
.progress .is-active { background: #050505; }
.app-icon {
position: absolute;
z-index: 3;
top: 313px;
left: 332px;
width: 120px;
height: 120px;
object-fit: contain;
transform: none;
}
h1 {
position: absolute;
z-index: 2;
top: 111px;
left: 40px;
width: 310px;
margin: 0;
font-family: "Mont", Arial, sans-serif;
font-size: 48px;
font-weight: 600;
letter-spacing: 0;
line-height: 52px;
}
.cta {
position: absolute;
z-index: 2;
right: 40px;
bottom: 40px;
left: 40px;
height: 58px;
border: 0;
border-radius: 999px;
color: white;
background: #000;
font: 500 20px/24px Mont, "Arial Rounded MT Bold", Arial, sans-serif;
cursor: pointer;
transition: transform .18s ease, background .18s ease;
}
.cta:hover { background: #191919; }
.cta:active { transform: scale(.97); }
.cta:focus-visible { outline: 2px solid white; outline-offset: -5px; }
</style>
<div class="screen-body">
<main class="onboarding" aria-labelledby="headline">
<img class="robot" src="https://flick-award-65707097.figma.site/_assets/v11/4f94f64504ce0aa87a72ede57dace8a3f878c98a.png" alt="" />
<nav class="progress" aria-label="Onboarding progress">
<span></span><span></span><span class="is-active"></span>
</nav>
<img class="app-icon" src="https://flick-award-65707097.figma.site/_assets/v11/c282fcce51bbc257dddef6e7bd172667eab47b07.png" alt="" />
<h1 id="headline">Speak the World's<br />Language!</h1>
<button class="cta" type="button">Get started</button>
</main>
</div>
</template>
</screen-preview>
</article>
</div>
</section>
</main>
<script>
class ScreenPreview extends HTMLElement {
connectedCallback() {
if (this.shadowRoot) return;
const template = this.querySelector(':scope > template');
if (!template) return;
const shadow = this.attachShadow({ mode: 'open' });
shadow.append(template.content.cloneNode(true));
template.remove();
if (this.dataset.screen === 'start') {
const button = shadow.querySelector('.cta');
button?.addEventListener('click', () => {
button.textContent = "Let\u2019s go!";
button.setAttribute('aria-label', 'Getting started');
});
}
}
}
customElements.define('screen-preview', ScreenPreview);
const gallery = document.querySelector('#screen-gallery');
const cards = [...document.querySelectorAll('.screen-card')];
let activeIndex = 0;
let frameRequest = 0;
let dragStartX = 0;
let dragStartScroll = 0;
let isDragging = false;
function fitScreensToViewport() {
const track = gallery.querySelector('.screen-track');
const trackStyles = getComputedStyle(track);
const paddingTop = Number.parseFloat(trackStyles.paddingTop) || 0;
const paddingBottom = Number.parseFloat(trackStyles.paddingBottom) || 0;
const paddingLeft = Number.parseFloat(trackStyles.paddingLeft) || 0;
const paddingRight = Number.parseFloat(trackStyles.paddingRight) || 0;
const availableHeight = Math.max(1, gallery.clientHeight - paddingTop - paddingBottom);
const availableWidth = Math.max(1, gallery.clientWidth - paddingLeft - paddingRight);
const scale = Math.min(1, availableHeight / 844, availableWidth / 390);
document.documentElement.style.setProperty('--screen-scale', scale.toFixed(5));
document.documentElement.style.setProperty('--rendered-screen-width', `${390 * scale}px`);
document.documentElement.style.setProperty('--rendered-screen-height', `${844 * scale}px`);
}
function nearestCardIndex() {
const galleryCenter = gallery.scrollLeft + gallery.clientWidth / 2;
let nearestIndex = 0;
let nearestDistance = Number.POSITIVE_INFINITY;
cards.forEach((card, index) => {
const cardCenter = card.offsetLeft + card.offsetWidth / 2;
const distance = Math.abs(cardCenter - galleryCenter);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestIndex = index;
}
});
return nearestIndex;
}
function updateActiveIndex() { activeIndex = nearestCardIndex(); }
function scrollToCard(index) {
const boundedIndex = Math.max(0, Math.min(cards.length - 1, index));
cards[boundedIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}
gallery.addEventListener('keydown', (event) => {
if (event.key === 'ArrowLeft') { event.preventDefault(); scrollToCard(activeIndex - 1); }
else if (event.key === 'ArrowRight') { event.preventDefault(); scrollToCard(activeIndex + 1); }
else if (event.key === 'Home') { event.preventDefault(); scrollToCard(0); }
else if (event.key === 'End') { event.preventDefault(); scrollToCard(cards.length - 1); }
});
gallery.addEventListener('scroll', () => {
cancelAnimationFrame(frameRequest);
frameRequest = requestAnimationFrame(updateActiveIndex);
}, { passive: true });
gallery.addEventListener('wheel', (event) => {
if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
const atStart = gallery.scrollLeft <= 0;
const atEnd = gallery.scrollLeft >= gallery.scrollWidth - gallery.clientWidth - 1;
if ((event.deltaY < 0 && atStart) || (event.deltaY > 0 && atEnd)) return;
event.preventDefault();
gallery.scrollLeft += event.deltaY;
}, { passive: false });
gallery.addEventListener('pointerdown', (event) => {
if (event.button !== 0 || event.target.closest('.screen-card')) return;
isDragging = true;
dragStartX = event.clientX;
dragStartScroll = gallery.scrollLeft;
gallery.classList.add('is-dragging');
gallery.setPointerCapture(event.pointerId);
});
gallery.addEventListener('pointermove', (event) => {
if (!isDragging) return;
gallery.scrollLeft = dragStartScroll - (event.clientX - dragStartX);
});
function stopDragging(event) {
if (!isDragging) return;
isDragging = false;
gallery.classList.remove('is-dragging');
if (gallery.hasPointerCapture(event.pointerId)) gallery.releasePointerCapture(event.pointerId);
}
gallery.addEventListener('pointerup', stopDragging);
gallery.addEventListener('pointercancel', stopDragging);
window.addEventListener('resize', () => {
fitScreensToViewport();
updateActiveIndex();
}, { passive: true });
fitScreensToViewport();
updateActiveIndex();
</script>
</body>
</html>最后更新于 2026/8/24 21:54:29