Add Effect v4 support, Fast Refresh tooling, and revamped docs #56
@@ -1,80 +1,652 @@
|
|||||||
.page {
|
.page {
|
||||||
|
--home-ink: #102f31;
|
||||||
|
--home-muted: #506a6c;
|
||||||
|
--home-teal: #08777b;
|
||||||
|
--home-teal-bright: #16a3a1;
|
||||||
|
--home-amber: #e99526;
|
||||||
|
--home-paper: #fffdf8;
|
||||||
|
--home-card: rgba(255, 255, 255, 0.76);
|
||||||
|
--home-line: rgba(16, 47, 49, 0.12);
|
||||||
min-height: calc(100vh - var(--ifm-navbar-height));
|
min-height: calc(100vh - var(--ifm-navbar-height));
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at top left, rgba(11, 111, 116, 0.22), transparent 32rem),
|
radial-gradient(circle at 8% 5%, rgba(14, 145, 148, 0.2), transparent 28rem),
|
||||||
radial-gradient(circle at 85% 20%, rgba(244, 166, 54, 0.18), transparent 28rem),
|
radial-gradient(circle at 92% 14%, rgba(240, 159, 48, 0.2), transparent 27rem),
|
||||||
linear-gradient(135deg, #f8f4ea 0%, #eef8f7 48%, #fffaf1 100%);
|
linear-gradient(145deg, #f7f2e7 0%, #edf8f6 48%, #fff8ed 100%);
|
||||||
|
color: var(--home-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gridBackdrop {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(16, 47, 49, 0.045) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(16, 47, 49, 0.045) 1px, transparent 1px);
|
||||||
|
background-size: 48px 48px;
|
||||||
|
inset: 0;
|
||||||
|
mask-image: linear-gradient(to bottom, black, transparent 62%);
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero {
|
.hero {
|
||||||
padding: 8rem 1rem 5rem;
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(3rem, 6vw, 6rem);
|
||||||
|
grid-template-columns: minmax(0, 0.88fr) minmax(560px, 1.12fr);
|
||||||
|
min-height: min(820px, calc(100vh - var(--ifm-navbar-height)));
|
||||||
|
padding-bottom: 6rem;
|
||||||
|
padding-top: 6rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow {
|
.heroCopy {
|
||||||
color: #0b6f74;
|
max-width: 650px;
|
||||||
font-size: 0.85rem;
|
}
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.16em;
|
.eyebrow,
|
||||||
margin-bottom: 1.25rem;
|
.sectionKicker {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--home-teal);
|
||||||
|
display: flex;
|
||||||
|
font-size: 0.76rem;
|
||||||
|
font-weight: 850;
|
||||||
|
gap: 0.65rem;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
margin: 0 0 1.35rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pulse {
|
||||||
|
background: var(--home-teal-bright);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 0 5px rgba(22, 163, 161, 0.14);
|
||||||
|
height: 0.55rem;
|
||||||
|
width: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
.hero h1 {
|
.hero h1 {
|
||||||
color: #132c2f;
|
color: var(--home-ink);
|
||||||
font-size: clamp(3rem, 9vw, 6.75rem);
|
font-size: clamp(3.4rem, 6.4vw, 6.4rem);
|
||||||
letter-spacing: -0.08em;
|
font-weight: 780;
|
||||||
line-height: 0.9;
|
letter-spacing: -0.075em;
|
||||||
|
line-height: 0.92;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
max-width: 880px;
|
}
|
||||||
|
|
||||||
|
.hero h1 span {
|
||||||
|
background: linear-gradient(105deg, var(--home-teal) 5%, #1a9793 48%, var(--home-amber));
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
display: inline;
|
||||||
|
-webkit-background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lede {
|
.lede {
|
||||||
color: #385256;
|
color: var(--home-muted);
|
||||||
font-size: clamp(1.15rem, 2vw, 1.45rem);
|
font-size: clamp(1.08rem, 1.55vw, 1.32rem);
|
||||||
line-height: 1.65;
|
line-height: 1.65;
|
||||||
margin: 2rem 0 0;
|
margin: 2rem 0 0;
|
||||||
max-width: 720px;
|
max-width: 630px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 0.8rem;
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards {
|
.primaryAction,
|
||||||
|
.secondaryAction,
|
||||||
|
.ctaAction {
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
display: inline-flex;
|
||||||
|
font-weight: 750;
|
||||||
|
gap: 0.8rem;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.88rem 1.35rem;
|
||||||
|
text-decoration: none !important;
|
||||||
|
transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primaryAction {
|
||||||
|
background: var(--home-ink);
|
||||||
|
box-shadow: 0 12px 28px rgba(16, 47, 49, 0.2);
|
||||||
|
color: #f6fffd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primaryAction:hover {
|
||||||
|
box-shadow: 0 16px 36px rgba(16, 47, 49, 0.28);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondaryAction {
|
||||||
|
background: rgba(255, 255, 255, 0.58);
|
||||||
|
border: 1px solid var(--home-line);
|
||||||
|
color: var(--home-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondaryAction:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: var(--home-teal);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.install {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--home-muted);
|
||||||
|
display: flex;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
gap: 0.7rem;
|
||||||
|
margin-top: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.install span {
|
||||||
|
color: var(--home-amber);
|
||||||
|
font-family: var(--ifm-font-family-monospace);
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.install code {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeStage {
|
||||||
|
min-width: 0;
|
||||||
|
perspective: 1200px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeGlow {
|
||||||
|
background: linear-gradient(115deg, rgba(11, 133, 136, 0.48), rgba(238, 156, 40, 0.38));
|
||||||
|
filter: blur(55px);
|
||||||
|
inset: 8% 5%;
|
||||||
|
opacity: 0.55;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow {
|
||||||
|
background: #101b1e;
|
||||||
|
border: 1px solid rgba(166, 225, 220, 0.2);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
box-shadow:
|
||||||
|
0 34px 90px rgba(18, 49, 52, 0.3),
|
||||||
|
0 2px 0 rgba(255, 255, 255, 0.06) inset;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
transform: rotateY(-2deg) rotateX(1deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowBar {
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
color: #b6c9c9;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1.25rem;
|
font-family: var(--ifm-font-family-monospace);
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
font-size: 0.73rem;
|
||||||
padding: 0 1rem 6rem;
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
padding: 0.78rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards article {
|
.windowDots {
|
||||||
background: rgba(255, 255, 255, 0.74);
|
display: flex;
|
||||||
border: 1px solid rgba(19, 44, 47, 0.12);
|
gap: 0.38rem;
|
||||||
border-radius: 1.5rem;
|
|
||||||
box-shadow: 0 24px 80px rgba(19, 44, 47, 0.08);
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards h2 {
|
.windowDots span {
|
||||||
color: #132c2f;
|
background: #4f6d6e;
|
||||||
font-size: 1.2rem;
|
border-radius: 50%;
|
||||||
margin-bottom: 0.75rem;
|
height: 0.55rem;
|
||||||
|
width: 0.55rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards p {
|
.windowDots span:first-child {
|
||||||
color: #486267;
|
background: #ef8d5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowDots span:nth-child(2) {
|
||||||
|
background: #e9bc50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowDots span:last-child {
|
||||||
|
background: #65bd92;
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowStatus {
|
||||||
|
color: #74d2cd;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.theme-code-block) {
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.theme-code-block pre) {
|
||||||
|
background: #101b1e !important;
|
||||||
|
color: #d7e5e4 !important;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.62;
|
||||||
|
max-height: none;
|
||||||
|
padding: 1.45rem 1.6rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token-line),
|
||||||
|
.codeWindow :global(.token.plain),
|
||||||
|
.codeWindow :global(.token.imports),
|
||||||
|
.codeWindow :global(.token.maybe-class-name),
|
||||||
|
.codeWindow :global(.token.punctuation),
|
||||||
|
.codeWindow :global(.token.operator),
|
||||||
|
.codeWindow :global(.token.plain-text),
|
||||||
|
.codeWindow :global(.token.property-access) {
|
||||||
|
color: #d7e5e4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token.keyword) {
|
||||||
|
color: #c792ea !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token.string) {
|
||||||
|
color: #c3e88d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token.function),
|
||||||
|
.codeWindow :global(.token.method) {
|
||||||
|
color: #82aaff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token.tag) {
|
||||||
|
color: #f07178 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.token.builtin) {
|
||||||
|
color: #ffcb6b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
color: #a9bcbc;
|
||||||
|
display: grid;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend span {
|
||||||
|
padding: 0.8rem 0.9rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend span + span {
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend b {
|
||||||
|
color: #e8a94b;
|
||||||
|
font-family: var(--ifm-font-family-monospace);
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featuresSection {
|
||||||
|
padding-bottom: 7rem;
|
||||||
|
padding-top: 4rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading {
|
||||||
|
display: grid;
|
||||||
|
gap: 0 3rem;
|
||||||
|
grid-template-columns: minmax(0, 1.25fr) minmax(280px, 0.75fr);
|
||||||
|
margin-bottom: 2.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading .sectionKicker {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading h2,
|
||||||
|
.cta h2 {
|
||||||
|
color: var(--home-ink);
|
||||||
|
font-size: clamp(2.25rem, 4vw, 4rem);
|
||||||
|
letter-spacing: -0.055em;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading > p:last-child {
|
||||||
|
align-self: end;
|
||||||
|
color: var(--home-muted);
|
||||||
|
font-size: 1.03rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureGrid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard {
|
||||||
|
background: var(--home-card);
|
||||||
|
border: 1px solid var(--home-line);
|
||||||
|
border-radius: 1.35rem;
|
||||||
|
color: inherit;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 310px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1.45rem;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none !important;
|
||||||
|
transition: border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard::before {
|
||||||
|
background: linear-gradient(90deg, var(--home-teal-bright), var(--home-amber));
|
||||||
|
content: "";
|
||||||
|
height: 3px;
|
||||||
|
left: 1.45rem;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 1.45rem;
|
||||||
|
top: 0;
|
||||||
|
transform: scaleX(0.5);
|
||||||
|
transition: opacity 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard:hover {
|
||||||
|
border-color: rgba(8, 119, 123, 0.35);
|
||||||
|
box-shadow: 0 24px 60px rgba(28, 65, 66, 0.12);
|
||||||
|
color: inherit;
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardTopline {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureIcon {
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(8, 119, 123, 0.1);
|
||||||
|
border: 1px solid rgba(8, 119, 123, 0.12);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
color: var(--home-teal);
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--ifm-font-family-monospace);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 850;
|
||||||
|
height: 2.7rem;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardIndex {
|
||||||
|
color: rgba(16, 47, 49, 0.32);
|
||||||
|
font-family: var(--ifm-font-family-monospace);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard h3 {
|
||||||
|
color: var(--home-ink);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
margin: 2rem 0 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard > p {
|
||||||
|
color: var(--home-muted);
|
||||||
|
font-size: 0.94rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardFooter {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardFooter code {
|
||||||
|
background: rgba(8, 119, 123, 0.08);
|
||||||
|
border: 0;
|
||||||
|
color: var(--home-teal);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardFooter > span {
|
||||||
|
color: var(--home-amber);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: transform 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard:hover .cardFooter > span {
|
||||||
|
transform: translate(3px, -3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaWrap {
|
||||||
|
padding-bottom: 7rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
align-items: center;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 90% 20%, rgba(233, 149, 38, 0.22), transparent 20rem),
|
||||||
|
linear-gradient(125deg, #12383a, #10292c);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 1.75rem;
|
||||||
|
box-shadow: 0 28px 80px rgba(16, 47, 49, 0.2);
|
||||||
|
display: flex;
|
||||||
|
gap: 3rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: clamp(2rem, 5vw, 4rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta .sectionKicker {
|
||||||
|
color: #79d5d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta h2 {
|
||||||
|
color: #f7fffd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta p:last-child {
|
||||||
|
color: #b9cccc;
|
||||||
|
line-height: 1.65;
|
||||||
|
margin: 1rem 0 0;
|
||||||
|
max-width: 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaAction {
|
||||||
|
background: #f0a13b;
|
||||||
|
color: #162d2e;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaAction:hover {
|
||||||
|
box-shadow: 0 14px 35px rgba(0, 0, 0, 0.24);
|
||||||
|
color: #102526;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .page {
|
||||||
|
--home-ink: #e9f7f5;
|
||||||
|
--home-muted: #a9bfbd;
|
||||||
|
--home-teal: #79d5d1;
|
||||||
|
--home-teal-bright: #5bc8c5;
|
||||||
|
--home-amber: #f1ad4e;
|
||||||
|
--home-card: rgba(20, 42, 45, 0.76);
|
||||||
|
--home-line: rgba(174, 221, 218, 0.14);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 8% 5%, rgba(33, 154, 154, 0.17), transparent 28rem),
|
||||||
|
radial-gradient(circle at 92% 14%, rgba(209, 130, 31, 0.13), transparent 27rem),
|
||||||
|
linear-gradient(145deg, #0f1b1e 0%, #102426 48%, #161e1e 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .gridBackdrop {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(174, 221, 218, 0.04) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(174, 221, 218, 0.04) 1px, transparent 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .secondaryAction {
|
||||||
|
background: rgba(19, 43, 46, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .secondaryAction:hover {
|
||||||
|
background: rgba(27, 57, 60, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .primaryAction {
|
||||||
|
background: #dff8f5;
|
||||||
|
color: #123638;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .primaryAction:hover {
|
||||||
|
color: #0b282a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .cardIndex {
|
||||||
|
color: rgba(220, 242, 239, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1180px) {
|
||||||
|
.hero {
|
||||||
|
gap: 3rem;
|
||||||
|
grid-template-columns: minmax(0, 0.85fr) minmax(500px, 1.15fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: clamp(3.2rem, 6vw, 5rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 996px) {
|
@media screen and (max-width: 996px) {
|
||||||
.hero {
|
.hero {
|
||||||
padding-top: 5rem;
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
padding-top: 4.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards {
|
.heroCopy,
|
||||||
grid-template-columns: 1fr;
|
.codeStage,
|
||||||
|
.codeWindow,
|
||||||
|
.codeWindow :global(.theme-code-block) {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heroCopy {
|
||||||
|
max-width: 760px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeStage {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 760px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureGrid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
.hero {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
max-width: 100vw;
|
||||||
|
padding-left: 1.15rem;
|
||||||
|
padding-right: 1.15rem;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heroCopy,
|
||||||
|
.codeStage {
|
||||||
|
max-width: calc(100vw - 2.3rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: clamp(3rem, 15vw, 4.3rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeading > p:last-child {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureGrid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureCard {
|
||||||
|
min-height: 275px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeLegend span + span {
|
||||||
|
border-left: 0;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowStatus {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.windowBar {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeWindow :global(.theme-code-block pre) {
|
||||||
|
font-size: 0.69rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 1.1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.featureCard,
|
||||||
|
.featureCard::before,
|
||||||
|
.cardFooter > span,
|
||||||
|
.primaryAction,
|
||||||
|
.secondaryAction,
|
||||||
|
.ctaAction {
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,203 @@
|
|||||||
import Link from "@docusaurus/Link"
|
import Link from "@docusaurus/Link"
|
||||||
|
import CodeBlock from "@theme/CodeBlock"
|
||||||
import Layout from "@theme/Layout"
|
import Layout from "@theme/Layout"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import type { ReactNode } from "react"
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
import styles from "./index.module.css"
|
import styles from "./index.module.css"
|
||||||
|
|
||||||
|
const componentExample = `import { Effect } from "effect"
|
||||||
|
import { Async, Component } from "effect-view"
|
||||||
|
import { Users } from "./services"
|
||||||
|
|
||||||
|
export const UserCard = Component.make("UserCard")(
|
||||||
|
function* ({ userId }: { userId: string }) {
|
||||||
|
const users = yield* Users
|
||||||
|
|
||||||
|
const user = yield* Component.useOnChange(
|
||||||
|
() => users.find(userId),
|
||||||
|
[userId],
|
||||||
|
)
|
||||||
|
|
||||||
|
yield* Component.useReactEffect(
|
||||||
|
() => Effect.forkScoped(users.watch(userId)),
|
||||||
|
[userId],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article>
|
||||||
|
<h2>{user.name}</h2>
|
||||||
|
<p>{user.email}</p>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
).pipe(Async.async)`
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
icon: "fx",
|
||||||
|
title: "Components are Effect programs",
|
||||||
|
description:
|
||||||
|
"Yield Effects and services directly from a component body, then return ordinary JSX. Types track every dependency all the way to the runtime boundary.",
|
||||||
|
tag: "Component.make",
|
||||||
|
to: "/docs/getting-started#write-your-first-component",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "R",
|
||||||
|
title: "One typed runtime",
|
||||||
|
description:
|
||||||
|
"Build your application Layer once. Components access HTTP clients, repositories, configuration, tracing, and your own services without React context plumbing.",
|
||||||
|
tag: "ReactRuntime",
|
||||||
|
to: "/docs/getting-started#create-the-runtime",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "S",
|
||||||
|
title: "Scopes follow React",
|
||||||
|
description:
|
||||||
|
"Every component owns an Effect Scope. Resources, subscriptions, and fibers are finalized when their component or dependency lifecycle ends.",
|
||||||
|
tag: "Scope.Scope",
|
||||||
|
to: "/docs/getting-started#the-component-root-scope",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "↔",
|
||||||
|
title: "Reactive state without a new universe",
|
||||||
|
description:
|
||||||
|
"Lens and View connect Effect state to React. Focus large models into small writable values and subscribe only where rendering needs them.",
|
||||||
|
tag: "Lens + View",
|
||||||
|
to: "/docs/state-management",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Q",
|
||||||
|
title: "TanStack-shaped server state",
|
||||||
|
description:
|
||||||
|
"Reactive keys, caching, stale times, background refresh, mutations, and invalidation—with typed Effect services, Causes, and scoped fibers underneath.",
|
||||||
|
tag: "Query + Mutation",
|
||||||
|
to: "/docs/query",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "Σ",
|
||||||
|
title: "Forms driven by Schema",
|
||||||
|
description:
|
||||||
|
"Keep input-friendly encoded values while application code receives validated, decoded types. Focus one root into reusable field-sized subforms.",
|
||||||
|
tag: "MutationForm + LensForm",
|
||||||
|
to: "/docs/forms",
|
||||||
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
export default function Home(): ReactNode {
|
export default function Home(): ReactNode {
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
title="effect-fc"
|
title="Effect View"
|
||||||
description="Write React function components with Effect"
|
description="Write React function components as typed Effect programs"
|
||||||
>
|
>
|
||||||
<main className={styles.page}>
|
<main className={styles.page}>
|
||||||
|
<div className={styles.gridBackdrop} aria-hidden="true" />
|
||||||
|
|
||||||
<section className={clsx("container", styles.hero)}>
|
<section className={clsx("container", styles.hero)}>
|
||||||
<p className={styles.eyebrow}>Effect for React function components</p>
|
<div className={styles.heroCopy}>
|
||||||
<h1>Write components as Effect programs.</h1>
|
<div className={styles.eyebrow}>
|
||||||
|
<span className={styles.pulse} />
|
||||||
|
Effect View for React 19
|
||||||
|
</div>
|
||||||
|
<h1>
|
||||||
|
React components,
|
||||||
|
<span> powered by Effect.</span>
|
||||||
|
</h1>
|
||||||
<p className={styles.lede}>
|
<p className={styles.lede}>
|
||||||
effect-fc gives React 19 components access to Effect services,
|
Bring typed services, scoped resources, reactive state, server
|
||||||
scopes, subscriptions, and async workflows without giving up normal
|
queries, and schema-driven forms into React without hiding either
|
||||||
React boundaries.
|
framework.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<Link className="button button--primary button--lg" to="/docs/getting-started">
|
<Link className={styles.primaryAction} to="/docs/getting-started">
|
||||||
Get Started
|
Start building
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
className="button button--secondary button--lg"
|
className={styles.secondaryAction}
|
||||||
to="https://github.com/Thiladev/effect-fc"
|
to="https://github.com/Thiladev/effect-fc"
|
||||||
>
|
>
|
||||||
GitHub
|
View on GitHub
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.install}>
|
||||||
|
<span aria-hidden="true">$</span>
|
||||||
|
<code>npm install effect-view effect@beta</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.codeStage}>
|
||||||
|
<div className={styles.codeGlow} aria-hidden="true" />
|
||||||
|
<div className={styles.codeWindow}>
|
||||||
|
<div className={styles.windowBar}>
|
||||||
|
<div className={styles.windowDots} aria-hidden="true">
|
||||||
|
<span />
|
||||||
|
<span />
|
||||||
|
<span />
|
||||||
|
</div>
|
||||||
|
<span>UserCard.tsx</span>
|
||||||
|
<span className={styles.windowStatus}>Effect + JSX</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock language="tsx">{componentExample}</CodeBlock>
|
||||||
|
<div className={styles.codeLegend}>
|
||||||
|
<span><b>01</b> Yield services</span>
|
||||||
|
<span><b>02</b> Own the lifecycle</span>
|
||||||
|
<span><b>03</b> Return JSX</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={clsx("container", styles.cards)}>
|
<section className={clsx("container", styles.featuresSection)}>
|
||||||
<article>
|
<div className={styles.sectionHeading}>
|
||||||
<h2>Generator components</h2>
|
<p className={styles.sectionKicker}>Why Effect View</p>
|
||||||
|
<h2>One model from render to resources.</h2>
|
||||||
<p>
|
<p>
|
||||||
Use <code>Component.make</code> to yield Effects and return JSX
|
Effect View adds the pieces React deliberately leaves open while
|
||||||
from the same component body.
|
preserving the React component model you already know.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</div>
|
||||||
<article>
|
|
||||||
<h2>Runtime at the edge</h2>
|
<div className={styles.featureGrid}>
|
||||||
|
{features.map((feature, index) => (
|
||||||
|
<Link
|
||||||
|
className={styles.featureCard}
|
||||||
|
to={feature.to}
|
||||||
|
key={feature.title}
|
||||||
|
>
|
||||||
|
<div className={styles.cardTopline}>
|
||||||
|
<span className={styles.featureIcon}>{feature.icon}</span>
|
||||||
|
<span className={styles.cardIndex}>
|
||||||
|
{String(index + 1).padStart(2, "0")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h3>{feature.title}</h3>
|
||||||
|
<p>{feature.description}</p>
|
||||||
|
<div className={styles.cardFooter}>
|
||||||
|
<code>{feature.tag}</code>
|
||||||
|
<span aria-hidden="true">↗</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className={clsx("container", styles.ctaWrap)}>
|
||||||
|
<div className={styles.cta}>
|
||||||
|
<div>
|
||||||
|
<p className={styles.sectionKicker}>Ready when React is</p>
|
||||||
|
<h2>Start with one Effect component.</h2>
|
||||||
<p>
|
<p>
|
||||||
Provide your app layer once with <code>ReactRuntime.Provider</code>
|
Add a runtime, cross the React boundary once, and grow into the
|
||||||
and convert Effect-FC components at React boundaries.
|
rest of the toolkit only when you need it.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</div>
|
||||||
<article>
|
<Link className={styles.ctaAction} to="/docs/getting-started">
|
||||||
<h2>Scoped lifecycles</h2>
|
Read the guide <span aria-hidden="true">→</span>
|
||||||
<p>
|
</Link>
|
||||||
Tie subscriptions and resources to component scopes so finalizers
|
</div>
|
||||||
run when React unmounts.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user