diff --git a/docusaurus.config.js b/docusaurus.config.js index 3ffa71d5..0222f17a 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -36,6 +36,29 @@ const config = { attributes: {}, innerHTML: plausibleInitScript, }, + { + tagName: 'link', + attributes: { + rel: 'preconnect', + href: 'https://fonts.googleapis.com', + crossorigin: 'anonymous', + }, + }, + { + tagName: 'link', + attributes: { + rel: 'preconnect', + href: 'https://fonts.gstatic.com', + crossorigin: 'anonymous', + }, + }, + { + tagName: 'link', + attributes: { + rel: 'stylesheet', + href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Space+Grotesk:wght@600&display=swap', + }, + }, ], presets: [ [ @@ -86,8 +109,9 @@ const config = { position: 'left', label: 'Documentation', }, - {to: 'https://leanpub.com/graphql-java/', label: 'Book', position: 'left'}, + {href: 'https://leanpub.com/graphql-java/', label: 'Book', position: 'left'}, {to: '/tutorials/getting-started-with-spring-boot', label: 'Tutorial', position: 'left'}, + {href: 'https://feddi.dev', label: 'Federation', position: 'left', className: 'navbar-federation-link'}, {to: '/blog', label: 'Blog', position: 'left'}, {to: '/security', label: 'Security', position: 'left'}, {to: '/about', label: 'About', position: 'left'}, diff --git a/src/components/ExtIcon.js b/src/components/ExtIcon.js new file mode 100644 index 00000000..e1532af6 --- /dev/null +++ b/src/components/ExtIcon.js @@ -0,0 +1,18 @@ +import React from 'react'; +import IconExternalLink from '@theme/Icon/ExternalLink'; + +const baseStyle = { + display: 'inline-flex', + alignItems: 'center', + verticalAlign: 'middle', + marginLeft: '3px', + position: 'relative', +}; + +export default function ExtIcon({ style }) { + return ( + + + + ); +} diff --git a/src/css/custom.css b/src/css/custom.css index 4f2d256b..ecc838f6 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -26,3 +26,33 @@ html[data-theme='dark'] .docusaurus-highlight-code-line { background-color: rgba(0, 0, 0, 0.3); } + +.navbar-federation-link { + color: #E91E8C !important; + font-weight: 500 !important; + background: rgba(233, 30, 140, 0.07) !important; + border-radius: 6px; + padding: 4px 10px !important; +} + +.navbar-federation-link:hover { + background: rgba(233, 30, 140, 0.13) !important; +} + +.navbar__link svg { + position: relative; + top: 1px; +} + +/* Version badge pill */ +.navbar__items--right .dropdown > .navbar__link { + font-family: ui-monospace, 'SFMono-Regular', Consolas, monospace; + background: #f4f4f4; + border-radius: 4px; + padding: 3px 8px; + font-size: 0.8rem; +} + +html[data-theme='dark'] .navbar__items--right .dropdown > .navbar__link { + background: rgba(255, 255, 255, 0.08); +} diff --git a/src/pages/index.js b/src/pages/index.js index 83f3b54e..aea61c9f 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,36 +1,94 @@ import React from 'react'; -import clsx from 'clsx'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import ExtIcon from '../components/ExtIcon'; import styles from './index.module.css'; -function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); +const EXT_LINK = { target: '_blank', rel: 'noopener noreferrer' }; + +function Hero() { return ( -
-
-

{siteConfig.title}

-

{siteConfig.tagline}

-
- - Buy the GraphQL Java book now - -
+
+

GraphQL Java

+

The Java implementation of GraphQL

+
+ Spring GraphQL + · + Netflix DGS + · + Atlassian + · + 1M+ downloads/month +
+
+ + Get started → + + + JVM-native GraphQL federation + +
+
+ ); +} + +function Card({ title, description, to, href, external }) { + const content = ( + <> +
+ {title}{external && }
-
+
{description}
+ + ); + if (to) { + return {content}; + } + return ( + + {content} + ); } export default function Home() { - const {siteConfig} = useDocusaurusContext(); return ( - - + + +
+
+
Documentation
+
+ + + + +
+
+ +
+
Federation
+
+
+
JVM-native GraphQL federation
+
+ Andreas Marek, creator of GraphQL Java, is building feddi — a JVM-native federation gateway for any team running GraphQL on the JVM. +
+
+ + feddi.dev + +
+
+ +
+
Community
+
+ + +
+
+
); } diff --git a/src/pages/index.module.css b/src/pages/index.module.css index 95cf017a..db9c1073 100644 --- a/src/pages/index.module.css +++ b/src/pages/index.module.css @@ -1,24 +1,228 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ - -.heroBanner { - padding: 4rem 0; +/* Hero */ +.hero { + background: #E91E8C; + color: white; text-align: center; - position: relative; - overflow: hidden; - height: 80vh; /* TODO remove this line when landing page copy is added */ + padding: 5rem 2rem; } -@media screen and (max-width: 966px) { - .heroBanner { - padding: 2rem; - } +.heroTitle { + font-family: 'Space Grotesk', sans-serif; + font-weight: 600; + font-size: 54px; + letter-spacing: -1.5px; + color: white; + margin: 0 0 0.75rem; + line-height: 1.1; } -.buttons { +.heroSubtitle { + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 20px; + color: rgba(255, 255, 255, 0.85); + margin: 0 0 1rem; +} + +.trustBar { + font-family: 'Inter', sans-serif; + font-size: 15px; + color: rgba(255, 255, 255, 0.65); display: flex; align-items: center; justify-content: center; + gap: 0.5rem; + flex-wrap: wrap; + margin-bottom: 2rem; +} + +.dot { + color: rgba(255, 255, 255, 0.3); +} + +.ctaRow { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; +} + +.cta { + font-family: 'Inter', sans-serif; + font-size: 16px; + padding: 0.75rem 1.5rem; + border-radius: 8px; + text-decoration: none; + display: inline-flex; + align-items: center; +} + +.ctaPrimary { + composes: cta; + background: white; + color: #E91E8C; + font-weight: 600; +} + +.ctaPrimary:hover { + background: rgba(255, 255, 255, 0.92); +} + +.ctaSecondary { + composes: cta; + background: transparent; + color: white; + border: 1.5px solid rgba(255, 255, 255, 0.5); +} + +.ctaSecondary:hover { + background: rgba(255, 255, 255, 0.08); +} + +/* Content */ +.content { + max-width: 1100px; + margin: 0 auto; + padding: 3.5rem 2rem; +} + +.section { + margin-bottom: 3rem; +} + +.sectionLabel { + font-family: 'Inter', sans-serif; + font-weight: 600; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #999; + margin-bottom: 1rem; +} + +/* Cards */ +.cardGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.75rem; +} + +.card { + border: 0.5px solid #e5e5e5; + border-radius: 8px; + padding: 14px 16px; + text-decoration: none; + display: block; + transition: border-color 0.15s, background 0.15s; +} + +.card:hover { + border-color: #ccc; + background: #fafafa; +} + +.cardTitle { + font-family: 'Inter', sans-serif; + font-weight: 500; + font-size: 16px; + color: var(--ifm-font-color-base); + margin-bottom: 4px; + display: flex; + align-items: center; +} + +.cardDesc { + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 15px; + color: #888; +} + +/* Federation card */ +.federationCard { + background: #fff5fa; + border: 0.5px solid #ffccdd; + border-radius: 8px; + padding: 18px 20px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.federationText { + flex: 1; +} + +.federationTitle { + font-family: 'Inter', sans-serif; + font-weight: 600; + font-size: 17px; + color: var(--ifm-font-color-base); + margin-bottom: 6px; +} + +.federationBody { + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 15px; + color: #666; + line-height: 1.5; +} + +.federationBtn { + background: #E91E8C; + color: white; + font-family: 'Inter', sans-serif; + font-weight: 500; + font-size: 15px; + border-radius: 6px; + padding: 8px 16px; + text-decoration: none; + white-space: nowrap; + flex-shrink: 0; + display: inline-flex; + align-items: center; +} + +.federationBtn:hover { + background: #d01a7e; +} + +/* Dark mode */ +:global([data-theme='dark']) .card { + border-color: var(--ifm-color-emphasis-300); +} + +:global([data-theme='dark']) .card:hover { + background: var(--ifm-color-emphasis-100); + border-color: var(--ifm-color-emphasis-400); +} + +:global([data-theme='dark']) .cardDesc { + color: var(--ifm-color-emphasis-700); +} + +:global([data-theme='dark']) .federationCard { + background: rgba(233, 30, 140, 0.08); + border-color: rgba(233, 30, 140, 0.25); +} + +:global([data-theme='dark']) .federationBody { + color: var(--ifm-color-emphasis-700); +} + +@media (max-width: 600px) { + .heroTitle { + font-size: 40px; + } + + .cardGrid { + grid-template-columns: 1fr; + } + + .federationCard { + flex-direction: column; + align-items: flex-start; + } } diff --git a/src/theme/Footer/index.js b/src/theme/Footer/index.js new file mode 100644 index 00000000..84c582bc --- /dev/null +++ b/src/theme/Footer/index.js @@ -0,0 +1,72 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import ExtIcon from '../../components/ExtIcon'; +import styles from './styles.module.css'; + +const EXT_LINK = { target: '_blank', rel: 'noopener noreferrer' }; +const LOGO_URL = '/img/logo.png'; +const YEAR = new Date().getFullYear(); + +function FooterExtIcon() { + return ; +} + +export default function Footer() { + return ( + + ); +} diff --git a/src/theme/Footer/styles.module.css b/src/theme/Footer/styles.module.css new file mode 100644 index 00000000..49e43aad --- /dev/null +++ b/src/theme/Footer/styles.module.css @@ -0,0 +1,165 @@ +.footer { + background: #fafafa; + border-top: 0.5px solid #e5e5e5; + padding: 2.5rem; +} + +.inner { + max-width: 1100px; + margin: 0 auto; +} + +.grid { + display: grid; + grid-template-columns: 2fr 1fr 1fr 1fr; + gap: 2rem; + margin-bottom: 2rem; +} + +.logoRow { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.logo { + width: 24px; + height: 24px; + object-fit: contain; +} + +.brandName { + font-family: 'Inter', sans-serif; + font-weight: 700; + font-size: 14px; + color: var(--ifm-font-color-base); +} + +.brandDesc { + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 15px; + color: #888; + line-height: 1.5; + margin: 0; +} + +.colHeader { + font-family: 'Inter', sans-serif; + font-weight: 600; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: #aaa; + margin-bottom: 0.75rem; +} + +.links { + list-style: none; + padding: 0; + margin: 0; +} + +.links li { + margin-bottom: 0.5rem; +} + +.link { + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 15px; + color: #555; + text-decoration: none; +} + +.link:hover { + color: var(--ifm-color-primary); + text-decoration: none; +} + +.federationLink { + color: #E91E8C !important; + background: rgba(233, 30, 140, 0.07); + border-radius: 6px; + padding: 3px 8px; + margin-left: -8px; +} + +.federationLink:hover { + color: #E91E8C !important; + background: rgba(233, 30, 140, 0.13); +} + +.bottom { + border-top: 0.5px solid #e5e5e5; + padding-top: 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.copyright { + font-size: 12px; + color: #aaa; + font-family: 'Inter', sans-serif; +} + +.license { + font-size: 12px; + color: #aaa; + text-decoration: none; + font-family: 'Inter', sans-serif; +} + +.license:hover { + color: #555; +} + +/* Dark mode */ +:global([data-theme='dark']) .footer { + background: var(--ifm-background-surface-color); + border-top-color: var(--ifm-color-emphasis-300); +} + +:global([data-theme='dark']) .brandDesc { + color: var(--ifm-color-emphasis-700); +} + +:global([data-theme='dark']) .link { + color: var(--ifm-color-emphasis-800); +} + +:global([data-theme='dark']) .link:hover { + color: var(--ifm-color-primary); +} + +:global([data-theme='dark']) .bottom { + border-top-color: var(--ifm-color-emphasis-300); +} + +:global([data-theme='dark']) .copyright, +:global([data-theme='dark']) .license { + color: var(--ifm-color-emphasis-600); +} + +@media (max-width: 768px) { + .footer { + padding: 2rem 1.5rem; + } + + .grid { + grid-template-columns: 1fr 1fr; + gap: 1.5rem; + } + + .brand { + grid-column: 1 / -1; + } +} + +@media (max-width: 480px) { + .grid { + grid-template-columns: 1fr; + } +}