feat: update select plan UI
parent
cf4fe5dc82
commit
ad33807627
|
|
@ -1,3 +1,72 @@
|
|||
::view-transition-group(*) {
|
||||
animation-duration: 200ms;
|
||||
animation-timing-function: cubic-bezier(0.25, 0, 0.5, 1);
|
||||
}
|
||||
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation-duration: 200ms;
|
||||
animation-timing-function: cubic-bezier(0.25, 0, 0.5, 1);
|
||||
}
|
||||
|
||||
@keyframes reveal {
|
||||
from {
|
||||
mask-position: 0% 200%;
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
mask-position: 0% 50%;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes reveal-reverse {
|
||||
from {
|
||||
mask-position: 0% 50%;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
mask-position: 0% 200%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::view-transition-new(terms) {
|
||||
animation: reveal 400ms cubic-bezier(0.25, 0, 0.5, 1) forwards;
|
||||
}
|
||||
|
||||
::view-transition-old(terms) {
|
||||
animation: reveal-reverse 200ms cubic-bezier(0.25, 0, 0.5, 1) forwards;
|
||||
}
|
||||
|
||||
::view-transition-new(action) {
|
||||
animation: fade-in 300ms cubic-bezier(0.25, 0, 0.5, 1) forwards;
|
||||
}
|
||||
|
||||
::view-transition-old(action) {
|
||||
animation: fade-out 150ms cubic-bezier(0.25, 0, 0.5, 1) forwards;
|
||||
}
|
||||
|
||||
[data-page="black"] {
|
||||
background: #000;
|
||||
min-height: 100vh;
|
||||
|
|
@ -8,14 +77,18 @@
|
|||
font-family: var(--font-mono);
|
||||
color: #fff;
|
||||
|
||||
[data-component="header-gradient"] {
|
||||
[data-component="header-logo"] {
|
||||
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.25)) drop-shadow(0 4px 16px rgba(0, 0, 0, 0.1));
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-light-rays {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 288px;
|
||||
inset: 0 0 auto 0;
|
||||
height: 30dvh;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(0, 0, 0, 0) 100%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
[data-component="header"] {
|
||||
|
|
@ -210,7 +283,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
padding: 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
|
|
@ -269,7 +342,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
padding: 24px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
|
|
@ -314,6 +387,10 @@
|
|||
flex-direction: column;
|
||||
gap: 8px;
|
||||
text-align: left;
|
||||
mask-image: linear-gradient(to bottom, transparent, black 25% 75%, transparent);
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: 0% 50%;
|
||||
mask-size: 100% 200%;
|
||||
|
||||
li {
|
||||
color: rgba(255, 255, 255, 0.59);
|
||||
|
|
@ -533,6 +610,7 @@
|
|||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
view-transition-name: fine-print;
|
||||
|
||||
a {
|
||||
color: rgba(255, 255, 255, 0.39);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { A, createAsync, RouteSectionProps } from "@solidjs/router"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
import { createMemo } from "solid-js"
|
||||
import { createMemo, createSignal } from "solid-js"
|
||||
import { github } from "~/lib/github"
|
||||
import { config } from "~/config"
|
||||
import LightRays, { defaultConfig, LightRaysControls, type LightRaysConfig } from "~/component/light-rays"
|
||||
import "./black.css"
|
||||
|
||||
export default function BlackLayout(props: RouteSectionProps) {
|
||||
|
|
@ -16,6 +17,8 @@ export default function BlackLayout(props: RouteSectionProps) {
|
|||
: config.github.starsFormatted.compact,
|
||||
)
|
||||
|
||||
const [lightRaysConfig, setLightRaysConfig] = createSignal<LightRaysConfig>(defaultConfig)
|
||||
|
||||
return (
|
||||
<div data-page="black">
|
||||
<Title>OpenCode Black | Access all the world's best coding models</Title>
|
||||
|
|
@ -39,7 +42,10 @@ export default function BlackLayout(props: RouteSectionProps) {
|
|||
content="Get access to Claude, GPT, Gemini and more with OpenCode Black subscription plans."
|
||||
/>
|
||||
<Meta name="twitter:image" content="/social-share-black.png" />
|
||||
<div data-component="header-gradient" />
|
||||
|
||||
<LightRays config={lightRaysConfig} class="header-light-rays" />
|
||||
<LightRaysControls config={lightRaysConfig} setConfig={setLightRaysConfig} />
|
||||
|
||||
<header data-component="header">
|
||||
<A href="/" data-component="header-logo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="179" height="32" viewBox="0 0 179 32" fill="none">
|
||||
|
|
|
|||
|
|
@ -43,11 +43,16 @@ export default function Black() {
|
|||
<div data-slot="pricing">
|
||||
<For each={plans}>
|
||||
{(plan) => (
|
||||
<button type="button" onClick={() => setSelected(plan.id)} data-slot="pricing-card">
|
||||
<div data-slot="icon">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => select(plan.id)}
|
||||
data-slot="pricing-card"
|
||||
style={{ "view-transition-name": `plan-card-${plan.id}` }}
|
||||
>
|
||||
<div data-slot="icon" style={{ "view-transition-name": `plan-icon-${plan.id}` }}>
|
||||
<PlanIcon plan={plan.id} />{" "}
|
||||
</div>
|
||||
<p data-slot="price">
|
||||
<p data-slot="price" style={{ "view-transition-name": `plan-price-${plan.id}` }}>
|
||||
<span data-slot="amount">${plan.id}</span> <span data-slot="period">per month</span>
|
||||
<Show when={plan.multiplier}>
|
||||
<span data-slot="multiplier">{plan.multiplier}</span>
|
||||
|
|
@ -57,25 +62,22 @@ export default function Black() {
|
|||
)}
|
||||
</For>
|
||||
</div>
|
||||
<p data-slot="fine-print">
|
||||
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||
</p>
|
||||
</Match>
|
||||
<Match when={selectedPlan()}>
|
||||
{(plan) => (
|
||||
<div data-slot="selected-plan">
|
||||
<div data-slot="selected-card">
|
||||
<div data-slot="icon">
|
||||
<div data-slot="selected-card" style={{ "view-transition-name": `plan-card-${plan().id}` }}>
|
||||
<div data-slot="icon" style={{ "view-transition-name": `plan-icon-${plan().id}` }}>
|
||||
<PlanIcon plan={plan().id} />
|
||||
</div>
|
||||
<p data-slot="price">
|
||||
<p data-slot="price" style={{ "view-transition-name": `plan-price-${plan().id}` }}>
|
||||
<span data-slot="amount">${plan().id}</span>{" "}
|
||||
<span data-slot="period">per person billed monthly</span>
|
||||
<Show when={plan().multiplier}>
|
||||
<span data-slot="multiplier">{plan().multiplier}</span>
|
||||
</Show>
|
||||
</p>
|
||||
<ul data-slot="terms">
|
||||
<ul data-slot="terms" style={{ "view-transition-name": "terms" }}>
|
||||
<li>Your subscription will not start immediately</li>
|
||||
<li>You will be added to the waitlist and activated soon</li>
|
||||
<li>Your card will be only charged when your subscription is activated</li>
|
||||
|
|
@ -84,8 +86,8 @@ export default function Black() {
|
|||
<li>Limits may be adjusted and plans may be discontinued in the future</li>
|
||||
<li>Cancel your subscription at anytime</li>
|
||||
</ul>
|
||||
<div data-slot="actions">
|
||||
<button type="button" onClick={() => setSelected(null)} data-slot="cancel">
|
||||
<div data-slot="actions" style={{ "view-transition-name": "action" }}>
|
||||
<button type="button" onClick={() => cancel()} data-slot="cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<a href={`/black/subscribe/${plan().id}`} data-slot="continue">
|
||||
|
|
@ -93,13 +95,13 @@ export default function Black() {
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p data-slot="fine-print">
|
||||
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
<p data-slot="fine-print" style={{ "view-transition-name": "fine-print" }}>
|
||||
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||
</p>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue