diff --git a/.gitignore b/.gitignore index 42f08a0e..0a4715ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ *.spv *.exrc +website/public diff --git a/website/sass/base.scss b/website/sass/base.scss index a793b85b..ac4a9b65 100644 --- a/website/sass/base.scss +++ b/website/sass/base.scss @@ -9,6 +9,35 @@ --max-width: 1600px; --max-width-plus-padding: calc(var(--max-width) + 40px * 2); --variable-px: Min(1px, 0.15vw); + --font-size-intro-heading: 60px; + --font-size-intro-body: 22px; + --font-size-link: 24px; + --font-size-heading: 40px; + --font-size-body: 18px; + + @media screen and (max-width: 760px) { + --font-size-intro-heading: 40px; + --font-size-intro-body: 18px; + --font-size-link: 20px; + --font-size-heading: 32px; + --font-size-body: 16px; + } + + @media screen and (max-width: 500px) { + --font-size-intro-heading: 32px; + --font-size-intro-body: 16px; + --font-size-link: 16px; + --font-size-heading: 28px; + --font-size-body: 16px; + } + + @media screen and (max-width: 400px) { + --font-size-intro-heading: 24px; + --font-size-intro-body: 14px; + --font-size-link: 14px; + --font-size-heading: 24px; + --font-size-body: 16px; + } } // Global element styles @@ -22,7 +51,7 @@ body { font-family: "Inter", sans-serif; line-height: 1.5; font-weight: 500; - font-size: 18px; + font-size: var(--font-size-body); color: var(--color-navy); } @@ -43,10 +72,10 @@ h1 { font-feature-settings: "lnum"; line-height: 1.25; font-weight: 700; - font-size: 60px; + font-size: var(--font-size-intro-heading); ~ p { - font-size: 22px; + font-size: var(--font-size-intro-body); } } @@ -55,11 +84,11 @@ h2 { font-feature-settings: "lnum"; line-height: 1.25; font-weight: 700; - font-size: 40px; + font-size: var(--font-size-heading); } h3 { - font-size: 24px; + font-size: var(--font-size-link); font-weight: 800; text-transform: uppercase; @@ -81,7 +110,7 @@ h4 { font-family: "Bona Nova", serif; font-feature-settings: "lnum"; line-height: 1.25; - font-size: 32px; + font-size: var(--font-size-heading); font-weight: 400; } @@ -105,7 +134,7 @@ p + p { .link { display: inline-block; - font-size: 24px; + font-size: var(--font-size-link); font-weight: 800; text-decoration: none; color: var(--color-crimson); @@ -120,10 +149,10 @@ p + p { color: var(--color-crimson); display: inline-block; border: 2px solid currentColor; - height: 48px; - line-height: calc(48px - 2 * 2px); - font-size: 24px; - padding: 0 24px; + height: calc(var(--font-size-link) * 2); + line-height: calc(var(--font-size-link) * 2 - 2 * 2px); + font-size: var(--font-size-link); + padding: 0 var(--font-size-link); box-sizing: border-box; text-decoration: none; font-weight: 800; @@ -189,6 +218,11 @@ p + p { display: block; width: 100%; height: auto; + + @media screen and (max-width: 800px) { + width: auto; + height: 120px; + } } } @@ -248,6 +282,13 @@ p + p { min-width: 280px; } + @media screen and (max-width: 600px) { + &.diptych .section, + &.triptych .section { + min-width: 100%; + } + } + img[alt=""]{ display: block; @@ -270,7 +311,7 @@ p + p { .page { box-sizing: border-box; overflow: hidden; - min-width: 600px; + min-width: 320px; header { padding: 0 40px; @@ -299,7 +340,10 @@ p + p { display: flex; justify-content: space-between; padding: 30px 0; - gap: 80px; + + @media screen and (max-width: 760px) { + padding: 20px 0; + } .left, .right { @@ -316,19 +360,18 @@ p + p { text-decoration: none; --height: 60px; --button-padding: 24px; - --font-size: 36px; - font-size: var(--font-size); + --nav-font-size: 32px; + font-size: var(--nav-font-size); &.button { height: var(--height); padding-left: var(--button-padding); padding-right: var(--button-padding); line-height: calc(var(--height) - 2 * 2px); - font-size: var(--font-size); + font-size: var(--nav-font-size); &::after { - content: "»"; - margin-left: 8px + content: " »"; } } @@ -339,34 +382,74 @@ p + p { height: var(--height); } } - } - @media screen and (max-width: 960px) { - gap: 60px; - - .left, - .right { + @media screen and (max-width: 960px) { gap: 30px; a { --height: 50px; --button-padding: 16px; - --font-size: 26px; + --nav-font-size: 26px; } } - } - - @media screen and (max-width: 760px) { - gap: 40px; - - .left, - .right { + + @media screen and (max-width: 760px) { gap: 20px; a { --height: 40px; - --button-padding: 16px; - --font-size: 22px; + --button-padding: 12px; + --nav-font-size: 22px; + } + } + + @media screen and (max-width: 600px) { + gap: 16px; + + a { + --height: 30px; + --button-padding: 8px; + --nav-font-size: 18px; + } + } + + @media screen and (max-width: 500px) { + gap: 12px; + + a { + --height: 24px; + --button-padding: 8px; + --nav-font-size: 16px; + } + } + + @media screen and (max-width: 460px) { + gap: 10px; + + a { + --height: 22px; + --button-padding: 6px; + --nav-font-size: 14px; + } + } + + @media screen and (max-width: 400px) { + gap: 8px; + + a { + --height: 20px; + --button-padding: 4px; + --nav-font-size: 12px; + } + } + + @media screen and (max-width: 360px) { + gap: 6px; + + a { + --height: 20px; + --button-padding: 4px; + --nav-font-size: 10px; } } } diff --git a/website/sass/index.scss b/website/sass/index.scss index 2dc94363..533c42ef 100644 --- a/website/sass/index.scss +++ b/website/sass/index.scss @@ -1,25 +1,40 @@ #logo { display: flex; + + img { + width: auto; + max-width: 100%; + max-height: 240px; + } +} - svg { - display: block; - height: auto; +.pencil-texture { + position: absolute; + --remaining-width-to-full: calc(var(--max-width-plus-padding) - min(calc(100vw - 100px), var(--max-width-plus-padding))); + left: Max(calc(-1 * var(--remaining-width-to-full)), -50px); + width: 100px; + mix-blend-mode: multiply; + + @media screen and (max-width: 1000px) { + width: 40px; + top: 400px; + left: -10px; } } #quick-links { margin-bottom: calc(120 * var(--variable-px)); display: flex; - gap: 20px; + gap: calc(var(--font-size-link) * 0.8); flex-wrap: wrap; } -.pencil-texture { - position: absolute; - --remaining-width-to-full: calc(var(--max-width-plus-padding) - min(calc(100vw - 100px), var(--max-width-plus-padding))); - left: Max(calc(-1 * var(--remaining-width-to-full)), -40px); - width: 100px; - mix-blend-mode: multiply; +#hero-message { + @media screen and (max-width: 1400px) { + p { + max-width: unset !important; + } + } } .hexagons { @@ -196,6 +211,13 @@ margin-right: auto; } } + + // 1600px is var(--max-width) + @media screen and (max-width: 1600px) { + .carousel.torn { + display: none; + } + } } #upcoming-tech { @@ -209,6 +231,7 @@ #community { #newsletter form { + width: 100%; margin-top: 40px; display: flex; gap: 20px; @@ -238,6 +261,11 @@ &.name { flex: 1 0 0; min-width: 240px; + + } + + &.phone { + display: none; } &.email { @@ -245,6 +273,13 @@ min-width: 240px; } + @media screen and (max-width: 400px) { + &.name, + &.email { + min-width: 100%; + } + } + &.submit { flex: 1 0 0; } @@ -255,7 +290,7 @@ } label { - font-size: 24px; + font-size: var(--font-size-link); font-weight: 800; margin-bottom: 10px; line-height: 1; @@ -264,13 +299,13 @@ input:not([type="submit"]) { flex: 0 0 auto; width: 100%; - height: 48px; - font-size: 22px; + height: calc(var(--font-size-link) * 2); + font-size: calc(var(--font-size-link) * 0.9); color: inherit; border: 2px solid currentColor; outline: none; margin: 0; - padding: 0 24px; + padding: 0 var(--font-size-link); font-family: inherit; font-weight: inherit; box-sizing: border-box; @@ -308,6 +343,11 @@ text-decoration: none; display: flex; + img { + width: 48px; + height: 48px; + } + span { line-height: 48px; margin-left: 20px; diff --git a/website/static/images/graphics/brush.svg b/website/static/images/graphics/brush.svg index eb34378c..b86e7487 100644 --- a/website/static/images/graphics/brush.svg +++ b/website/static/images/graphics/brush.svg @@ -1,4 +1,4 @@ - + diff --git a/website/static/images/graphics/discord.svg b/website/static/images/graphics/discord.svg new file mode 100644 index 00000000..8c5a492d --- /dev/null +++ b/website/static/images/graphics/discord.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/static/images/graphics/github.svg b/website/static/images/graphics/github.svg new file mode 100644 index 00000000..8db89656 --- /dev/null +++ b/website/static/images/graphics/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/static/images/graphics/logotype.svg b/website/static/images/graphics/logotype.svg new file mode 100644 index 00000000..13da53e0 --- /dev/null +++ b/website/static/images/graphics/logotype.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/website/static/images/graphics/reddit.svg b/website/static/images/graphics/reddit.svg new file mode 100644 index 00000000..14ca813b --- /dev/null +++ b/website/static/images/graphics/reddit.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/static/images/graphics/twitter.svg b/website/static/images/graphics/twitter.svg new file mode 100644 index 00000000..7c1b035d --- /dev/null +++ b/website/static/images/graphics/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/static/js/carousel.js b/website/static/js/carousel.js index 2294a8b8..db3ca0de 100644 --- a/website/static/js/carousel.js +++ b/website/static/js/carousel.js @@ -1,14 +1,17 @@ +const FLING_VELOCITY_THRESHOLD = 10; +const FLING_VELOCITY_WINDOW_SIZE = 20; + let carouselImages; let carouselDirectionPrev; let carouselDirectionNext; let carouselDots; let carouselDescriptions; let carouselDragLastClientX; -let velocityDeltaWindow = Array.from({ length: 20 }, () => ({ time: 0, delta: 0 })); +let velocityDeltaWindow = Array.from({ length: FLING_VELOCITY_WINDOW_SIZE }, () => ({ time: 0, delta: 0 })); window.addEventListener("DOMContentLoaded", initializeCarousel); -window.addEventListener("pointerup", dragEnd); -window.addEventListener("scroll", dragEnd); +window.addEventListener("pointerup", () => dragEnd(false)); +window.addEventListener("scroll", () => dragEnd(true)); window.addEventListener("pointermove", dragMove); function initializeCarousel() { @@ -87,7 +90,7 @@ function dragBegin(event) { document.querySelector("#screenshots").classList.add("dragging"); } -function dragEnd() { +function dragEnd(dropWithoutVelocity) { if (!carouselImages) return; carouselDragLastClientX = undefined; @@ -108,7 +111,7 @@ function dragEnd() { const activeDotIndex = currentActiveDotIndex(); // If the speed is fast enough, slide to the next or previous image in that direction - if (Math.abs(recentVelocity) > 10) { + if (Math.abs(recentVelocity) > FLING_VELOCITY_THRESHOLD && !dropWithoutVelocity) { // Positive velocity should go to the previous image if (recentVelocity > 0) { // Don't apply the velocity-based fling if we're already snapping to the next image @@ -128,6 +131,7 @@ function dragEnd() { } // If we didn't slide in a direction due to clear velocity, just snap to the closest image + // This can be reached either by not entering the if statement above, or by its inner if statements not returning early and exiting back to this scope slideTo(clamp(closestImageIndex, 0, carouselDots.length - 1), true); } @@ -138,7 +142,7 @@ function dragMove(event) { const LEFT_MOUSE_BUTTON = 1; if (!(event.buttons & LEFT_MOUSE_BUTTON)) { - dragEnd(); + dragEnd(false); return; } diff --git a/website/static/js/navbar.js b/website/static/js/navbar.js index 466ad066..e565c3a9 100644 --- a/website/static/js/navbar.js +++ b/website/static/js/navbar.js @@ -15,40 +15,6 @@ let activeRippleIndex; window.addEventListener("DOMContentLoaded", initializeRipples); window.addEventListener("resize", () => animate(true)); -function setRipples(mediaQueryScaleFactor) { - const rippleSvgRect = rippleSvg.getBoundingClientRect(); - const rippleSvgLeft = rippleSvgRect.left; - const rippleSvgWidth = rippleSvgRect.width; - - let path = `M 0,${fullRippleHeight + 3} `; - - ripples.forEach((ripple) => { - if (!ripple.animationStartTime || !ripple.animationEndTime) return; - - const t = Math.min((Date.now() - ripple.animationStartTime) / (ripple.animationEndTime - ripple.animationStartTime), 1); - const height = fullRippleHeight * (ripple.goingUp ? ease(t) : 1 - ease(t)); - - const buttonRect = ripple.element.getBoundingClientRect(); - - const buttonCenter = buttonRect.width / 2; - const rippleCenter = RIPPLE_WIDTH / 2 * mediaQueryScaleFactor; - const rippleOffset = rippleCenter - buttonCenter; - - const rippleStartX = buttonRect.left - rippleSvgLeft - rippleOffset; - - const rippleRadius = RIPPLE_WIDTH / 2 * mediaQueryScaleFactor; - const handleRadius = rippleRadius * HANDLE_STRETCH; - - path += `L ${rippleStartX},${fullRippleHeight + 3} `; - path += `c ${handleRadius},0 ${rippleRadius - handleRadius},-${height} ${rippleRadius},-${height} `; - path += `s ${rippleRadius - handleRadius},${height} ${rippleRadius},${height} `; - }); - - path += `l ${rippleSvgWidth},0`; - - ripplePath.setAttribute("d", path); -} - function initializeRipples() { ripplesInitialized = true; @@ -110,6 +76,40 @@ function animate(forceRefresh) { } } +function setRipples(mediaQueryScaleFactor) { + const rippleSvgRect = rippleSvg.getBoundingClientRect(); + const rippleSvgLeft = rippleSvgRect.left; + const rippleSvgWidth = rippleSvgRect.width; + + let path = `M 0,${fullRippleHeight + 3} `; + + ripples.forEach((ripple) => { + if (!ripple.animationStartTime || !ripple.animationEndTime) return; + + const t = Math.min((Date.now() - ripple.animationStartTime) / (ripple.animationEndTime - ripple.animationStartTime), 1); + const height = fullRippleHeight * (ripple.goingUp ? ease(t) : 1 - ease(t)); + + const buttonRect = ripple.element.getBoundingClientRect(); + + const buttonCenter = buttonRect.width / 2; + const rippleCenter = RIPPLE_WIDTH / 2 * mediaQueryScaleFactor; + const rippleOffset = rippleCenter - buttonCenter; + + const rippleStartX = buttonRect.left - rippleSvgLeft - rippleOffset; + + const rippleRadius = RIPPLE_WIDTH / 2 * mediaQueryScaleFactor; + const handleRadius = rippleRadius * HANDLE_STRETCH; + + path += `L ${rippleStartX},${fullRippleHeight + 3} `; + path += `c ${handleRadius},0 ${rippleRadius - handleRadius},-${height} ${rippleRadius},-${height} `; + path += `s ${rippleRadius - handleRadius},${height} ${rippleRadius},${height} `; + }); + + path += `l ${rippleSvgWidth},0`; + + ripplePath.setAttribute("d", path); +} + function ease(x) { return 1 - (1 - x) * (1 - x); } diff --git a/website/templates/404.html b/website/templates/404.html index 5ca96b89..384c27ff 100644 --- a/website/templates/404.html +++ b/website/templates/404.html @@ -2,7 +2,7 @@ {% block title %}Page not found.{% endblock title %} {% block content %} -
+

Page not found (yet).

diff --git a/website/templates/base.html b/website/templates/base.html index e5b1c238..fb0af2b6 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -2,7 +2,7 @@ - + Graphite | {% block title %}{% endblock title %} {% block head %}{% endblock head %} diff --git a/website/templates/index.html b/website/templates/index.html index c75efc56..8ca2fcf8 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -8,33 +8,7 @@ {% block content %}

@@ -98,7 +72,7 @@

View of the current alpha version of the Graphite editor with a blank canvas. Try this out at editor.graphite.rs - right in your browser. Send in your artwork to potentially be featured here in place of this blank canvas.

+ instantly in your browser. Send in your artwork to potentially be featured here in place of this blank canvas.

Interface mockup showcasing a photo editing project that utilizes Graphite's raster graphics pipeline, one of the upcoming roadmap milestones. Photo editing is not yet supported.

This last screenshot is currently identical to the previous one, serving as a placeholder. Work is ongoing to replace this image with a node graph mockup.

@@ -109,7 +83,7 @@
- +

Professional 2D content creation for everyone.

@@ -117,7 +91,7 @@ With great power comes great accessibility. Graphite is built on the belief that the best creative tools can be powerful and within reach of all.

- The software is designed with a friendly and intuitive interface where a delightful user experience is of first-class importance. It is available for free under an open source license and usable instantly through a web browser or an upcoming native client on Windows, Mac, and Linux.

@@ -131,7 +105,7 @@
- +

Available now for alpha testing.

@@ -197,7 +171,7 @@ color handling (ACES/OpenColorIO). Real-time collaboration. A rich ecosystem of custom nodes.

- Learn more about the planned technology in forthcoming Graphite releases. + Learn more about the planned technology in forthcoming Graphite releases:

Features
@@ -207,7 +181,7 @@
- +

Built for the future, powered by Rust.

@@ -249,15 +223,19 @@ Graphite is early in development and the most exciting, ambitious features are still to come. Enter your email below to receive only occasional announcements when major updates are ready for your enjoyment. The first newsletter will likely be sent out for the Graphite alpha milestone 2 release, featuring node-based editing, later this year.

-
+
- + +
+
+ +