diff --git a/assets/main.scss b/assets/main.scss index 92982dff..02b31ceb 100644 --- a/assets/main.scss +++ b/assets/main.scss @@ -47,7 +47,6 @@ $footertextmuted: #aaaaaa; $postlinktext: #333333; $maxwidth: 1200px; -$maxwidthcover: 1200px; $maxwidthwide: 1400px; $contentwidth: 80%; $contentwidthwide: 92%; @@ -101,7 +100,7 @@ $eco-hues: } } -@each $icon in zulip, discourse, bluesky { +@each $icon in zulip, discourse, bluesky, xcom { .i-#{$icon}::before { display: inline-block; height: 1rem; @@ -120,28 +119,15 @@ $eco-hues: } } -.i-xcom::before { - content: "𝕏"; - font-weight: 900; - transition: - background 200ms ease-out, - filter 200ms ease-out; -} - .card:hover { // Not discord, since it’s light-colored .i-zulip::before, - .i-bluesky::before { + .i-bluesky::before, + .i-xcom::before { filter: invert(100%); } } -// Cover buttons -#badges .btn { - background-color: $tilebg; - border: none; -} - #join-content .card { h5 { font-weight: 700; @@ -154,32 +140,26 @@ $eco-hues: } } -#badges .btn-github:hover, #join-content .card#github:hover { background-color: $github-black !important; color: white !important; } -#badges .btn-discourse:hover, #join-content .card#discourse:hover { background-color: $discourse-yellow !important; color: black !important; } -#badges .btn-zulip:hover, #join-content .card#zulip:hover { background-color: $zulip-blue !important; color: white !important; } -#badges .btn-twitter:hover, #join-content .card#twitter:hover { background-color: $twitter-blue !important; color: white !important; } -#badges .btn-bluesky:hover, #join-content .card#bluesky:hover { background-color: $bluesky-blue !important; color: white !important; } -#badges .btn-youtube:hover, #join-content .card#youtube:hover { background-color: $youtube-red !important; color: white !important; @@ -277,6 +257,50 @@ body { } } +.nav-social { + display: flex; + align-items: center; + gap: 0.15rem; + padding: 0.5rem 0.25rem !important; + + // Bootstrap's own collapse breakpoint (992px) isn't wide enough to also fit this row, so it overflows the nav between 992px and roughly 1180px; hide it there and rely on the footer. + @media (max-width: 1199px) { + display: none; + } + + a { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: 50%; + color: $navtext; + font-size: 1rem; + text-decoration: none; + transition: all 200ms ease-in-out; + + &:hover { + background-color: $navbghover; + color: $navtexthover; + + i::before { + opacity: 1; + } + } + + // Both icon kinds are 1rem boxes, but the Bootstrap glyphs are baseline-aligned + // (`vertical-align: -.125em`) while our `::before` images aren't. Making the `` + // a 1rem flex box takes the baseline out of it so all six line up. + i { + display: flex; + width: 1rem; + height: 1rem; + line-height: 1; + } + } +} + #join-button { color: white; background-color: #4557c4; @@ -352,17 +376,18 @@ body { align-items: center; align-self: center; #cover { - max-width: $maxwidthcover; + max-width: $maxwidthwide; width: 100%; padding: 0 1rem; #cover-top-section { - margin-top: 3rem; + margin-top: 0.5rem; padding: 2rem 0; } #cover-heading { - margin: 0; + margin: 0 auto; padding: 0; - max-width: $maxwidthcover; + max-width: $maxwidthwide; + text-align: center; #title { #title-text { #title-name { @@ -386,105 +411,194 @@ body { } } } - #badges { + + #hero-lede { + margin: 1.5rem auto 0; + font-size: 1.1rem; + line-height: 1.6; + color: $tiletext2; + @media (max-width: 50rem) { + font-size: 0.95rem; + } + } + + #hero-points { display: flex; flex-wrap: wrap; - gap: 1rem; - margin: 2rem 0; + justify-content: center; + gap: 2rem; + margin: 2rem auto 0; @media (max-width: 50rem) { - margin: 1rem 0; - padding: 0; - width: 100%; + gap: 1.25rem; + margin-top: 1.5rem; + } + .hero-point { + flex: 1 1 14rem; + max-width: 18rem; + display: flex; + flex-direction: column; + align-items: center; + text-decoration: none; + border-radius: 1rem; + padding: 1.25rem 1rem; + transition: all 200ms ease-in-out; + + &:hover { + background-color: $tilebg4; + transform: translateY(-2px); + + .hero-point-icon { + transform: scale(1.08); + } + } + } + .hero-point-icon { + width: 3rem; + height: 3rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + margin-bottom: 0.85rem; + font-size: 1.35rem; + background-color: rgba($spatialdatablue, 0.16); + color: darken($spatialdatablue, 12%); + transition: transform 200ms ease-in-out; + } + .hero-point-title { + font-size: 1.15rem; + font-weight: 800; + margin-bottom: 0.4rem; + color: $tiletext; + } + .hero-point-text { + margin: 0; + font-size: 0.9rem; + color: $tiletext2; } } } #chip-announcement { margin: 0 auto 2rem; - max-width: $maxwidthcover; + max-width: $maxwidthwide; width: 100%; @media (max-width: 50rem) { - width: 100%; - margin: 0 auto 1.25rem; + margin: 0 auto 1.5rem; } + } + + .conference-banner { display: flex; - justify-content: center; + align-items: center; + gap: 1.75rem; + width: 92%; + margin: 0 auto; + padding: 1rem 1.75rem; + border-radius: 1rem; + background: linear-gradient(120deg, #0b0b12 0%, #22223a 100%); + color: white; + text-decoration: none; + transition: all 200ms ease-in-out; + &:hover { - cursor: pointer; - } + transform: translateY(-2px); + box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.25); - .event-chip { - width: auto; - display: inline-flex; - align-items: center; - background-color: transparent; - border-radius: 2.2rem; - padding: 0.1rem 1rem 0.1rem 0; - font-size: 0.8rem; - font-weight: 500; - color: black; - transition: all 0.3s ease; - outline: 1px solid black; + .conference-banner-cta { + background-color: white; + color: black; - @media (max-width: 50rem) { - border-radius: 1.5rem; - padding: 0.05rem 0.6rem 0.05rem 0; - font-size: 0.7rem; + .arrow { + transform: translateX(0.2rem); + } } + } - .event-date { - background-color: black; - color: white; - padding: 0.5rem 1rem; - border-radius: 2rem; - margin: 0.1rem 1rem 0.1rem 0.2rem; - align-self: start; + @media (max-width: 50rem) { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + padding: 1.25rem 1.5rem; + text-align: left; + } + } - @media (max-width: 50rem) { - padding: 0.35rem 0.7rem; - border-radius: 1.5rem; - margin: 0.05rem 0.7rem 0.05rem 0.1rem; - } - } + .conference-banner-date { + display: flex; + flex-direction: column; + align-items: center; + flex-shrink: 0; + line-height: 1.1; + padding-right: 1.75rem; + border-right: 1px solid rgba(255, 255, 255, 0.2); - .event-text { - position: relative; + .conference-banner-month { + font-size: 0.85rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: rgba(255, 255, 255, 0.6); + } - .arrow { - display: inline-block; - transition: transform 0.3s ease; - } - } + .conference-banner-day { + font-size: 1.6rem; + font-weight: 800; + } - &:hover { - color: #4557c4; - transform: scale(1.05); + .conference-banner-year { + font-size: 0.85rem; + color: rgba(255, 255, 255, 0.6); + } - .event-date { - background: linear-gradient(135deg, #262fb5, #74c8fa); - } + @media (max-width: 50rem) { + flex-direction: row; + align-items: baseline; + gap: 0.4rem; + padding-right: 0; + border-right: none; + } + } - .event-text .arrow { - transform: translateX(0.2rem); - } - } + .conference-banner-body { + flex: 1; + min-width: 0; - position: relative; + .conference-banner-title { + font-size: 1.15rem; + font-weight: 800; + } - a { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - } + .conference-banner-desc { + margin: 0.2rem 0 0; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.75); + max-width: 34rem; + } + } + + .conference-banner-cta { + flex-shrink: 0; + padding: 0.7rem 1.4rem; + border-radius: 2rem; + background-color: rgba(255, 255, 255, 0.12); + font-weight: 700; + white-space: nowrap; + transition: all 200ms ease-in-out; + + .arrow { + display: inline-block; + transition: transform 200ms ease; + } + + @media (max-width: 50rem) { + align-self: flex-start; } } section { - max-width: $maxwidth; - width: $contentwidth; + max-width: $maxwidthwide; + width: $contentwidthwide; margin: 0 auto; margin-top: 5%; @media (max-width: 50rem) { @@ -514,98 +628,137 @@ body { // The wide gap under the hero used to live on #packages, which put it // between whatever section came after the demo and the packages. #interactive-demo-section { - margin-bottom: 15%; + // Overrides the generic `section` rule's margin-top: 5%, which stacked with #cover-top-section's padding into an oversized gap. + margin-top: 2rem; + margin-bottom: 3rem; } - #packages { - #packages-list { - .card { - display: flex; - flex-direction: row; - align-items: center; - background-color: $tilebg; - border-radius: 0.5rem; - transition: all 200ms ease-in-out; - @media (max-width: 50rem) { - min-height: 6rem; - } - .package-icon { - transition: all 200ms ease-in-out; - align-items: center; - justify-content: center; - padding: auto; - display: flex; - min-width: 6rem; - max-width: 6rem; - // Matches the img height below, so that a package - // without an icon still lines up with the others. - min-height: 4rem; - @media (max-width: 50rem) { - display: none; - } + #feature-grid { + background-color: $tilebg4; + border-radius: 1.25rem; + padding: 2.5rem; - img { - max-width: 4rem; - height: 4rem; - } - } - .package-text { - .package-name { - color: $tiletext; - } - .package-desc { - font-size: 0.9rem; - color: $tiletext2; - } - } - &:hover { - cursor: pointer; - background-color: $tilebg2; - .package-icon { - transform: scale(1.1); - } - transform: scale(1.05); - } - &#anndata-tile:hover { - background-color: rgba($anndataorange, 0.2); - } - &#scanpy-tile:hover { - background-color: rgba($scanpymandy, 0.2); - } - &#mudata-tile:hover { - background-color: rgba($mudatagreen, 0.2); - } - &#muon-tile:hover { - background-color: rgba($muonaquamarine, 0.2); - } + @media (max-width: 50rem) { + padding: 1.5rem; + } + + .section-heading { + margin-bottom: 1.5rem; + } + + #feature-grid-items { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: 3rem; + row-gap: 2rem; + + @media (max-width: 50rem) { + grid-template-columns: 1fr; + row-gap: 1.5rem; } } + + .feature-item { + display: flex; + gap: 1rem; + align-items: flex-start; + } + + .feature-item-icon { + flex-shrink: 0; + width: 2.25rem; + height: 2.25rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 1.1rem; + background-color: rgba($spatialdatablue, 0.16); + color: darken($spatialdatablue, 12%); + } + + .feature-item-title { + font-weight: 700; + color: $tiletext; + margin-bottom: 0.2rem; + } + + .feature-item-text { + margin: 0; + font-size: 0.9rem; + color: $tiletext2; + } } - #mission { - .section-content { - p { - font-size: 1.1rem; + #about-highlights { + .about-card-grid { + display: grid; + // A fixed 3-column count rather than auto-fit: on a wide viewport auto-fit would squeeze in a 4th column and break the 3+3 layout of 6 cards. + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.5rem; + + @media (max-width: 64rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + @media (max-width: 50rem) { + grid-template-columns: 1fr; + gap: 1rem; } } - } - #team { - .section-content { - .section-paragraph { - #team-list { - display: inline; - } - a { - border-bottom: 1px solid $linkunderline; - transition: - border 200ms, - color 200ms; - - &:hover, - &:active { - color: black; - border-bottom: 1px solid black; - } - } + + .about-card { + display: flex; + flex-direction: column; + background-color: $tilebg4; + border: 1px solid $overline; + border-radius: $card-radius; + padding: 1.75rem; + transition: all 200ms ease-in-out; + + &:hover { + border-color: $tilebg2; + box-shadow: 0 0.6rem 1.5rem rgba(0, 0, 0, 0.06); + transform: translateY(-2px); } + + // Pins the "more" link to the card's bottom edge regardless of how many lines the paragraph above it takes. + > a { + margin-top: auto; + } + } + + .about-card-icon { + width: 2.75rem; + height: 2.75rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + margin-bottom: 1rem; + font-size: 1.3rem; + background-color: $tilebg; + color: $tiletext; + } + + // Written as a descendant rather than nested under .about-card-icon: `&` there would carry the whole ancestor chain and put .about-card in front of #about-highlights. + @each $hue in $eco-hues { + .about-card[data-hue="#{index($eco-hues, $hue) - 1}"] + .about-card-icon { + background-color: rgba($hue, 0.16); + color: darken($hue, 12%); + } + } + + .about-card-title { + font-size: 1.2rem; + font-weight: 700; + margin: 0 0 0.5rem; + color: $tiletext; + } + + .about-card-text { + font-size: 1rem; + color: $tiletext2; + margin-bottom: 0.75rem; } } } @@ -3108,7 +3261,7 @@ body { .interactive-container { width: 100%; - max-width: 1200px; + max-width: $maxwidthwide; margin: 0 auto; transform-style: preserve-3d; perspective: 1500px; diff --git a/content/_index.md b/content/_index.md index 996bc1ed..74e40d0c 100644 --- a/content/_index.md +++ b/content/_index.md @@ -2,24 +2,92 @@ title = "scverse" description = "Foundational tools for single-cell omics data analysis" +# Hero +[hero] + +text = """\ +Scverse is an open-source, community-governed ecosystem of packages for single-cell and spatial omics. Shared data structures like AnnData, MuData, and SpatialData let tools such as Scanpy, scvi-tools, and Squidpy compose into a single powerful analysis.""" + +[[hero.points]] +icon = "bi-mortarboard" +title = "For researchers" +text = "Well-documented, actively maintained tools that interoperate out of the box." +link = "/learn" + +[[hero.points]] +icon = "bi-code-slash" +title = "For developers" +text = "Shared data structures and conventions, so your package fits the ecosystem from day one." +link = "/join" + +[[hero.points]] +icon = "bi-bank" +title = "For institutions" +text = "A community-governed foundation ensuring the long-term maintenance of critical infrastructure." +link = "/about/mission" + +# Features +[features] + +[[features.items]] +icon = "bi-book" +title = "Documentation" +text = "Extensive documentation covering installation, usage, and API references for every core package." + +[[features.items]] +icon = "bi-tags" +title = "Stable releases" +text = "Semantic versioning and changelogs for every release." + +[[features.items]] +icon = "bi-unlock" +title = "Open source" +text = "Freely licensed and hosted on GitHub β€” use it, modify it, fork it." + +[[features.items]] +icon = "bi-check2-circle" +title = "Continuously tested" +text = "Every change runs through CI before release." + +[[features.items]] +icon = "bi-plug" +title = "Interoperable" +text = "Shared data structures mean output from one tool works as input to the next." + +[[features.items]] +icon = "bi-people" +title = "Community governed" +text = "Roadmap and priorities are set by the community and core developers, not a single company." + +# Core packages +[packages] + +text = """\ +scverse develops a set of interoperable packages spanning every step of single-cell and spatial omics analysis, from data structures to modeling.""" + +# Events +[events] + +text = """\ +scverse hosts hackathons, workshops, and an annual conference to bring the community together throughout the year.""" # Mission [mission] text = """\ -Scverse is a consortium of foundational tools (mostly in Python) for omics data in life sciences. It has been founded to ensure the long-term maintenance of these core tools.""" +Scverse builds the community that sustains foundational tools for omics data in the life sciences.""" # Ecosystem [ecosystem] text = """\ -A broader ecosystem of packages builds on the scverse core packages. [These tools](/packages/#ecosystem) implement models and analytical approaches to tackle challenges in spatial omics, regulatory genomics, trajectory inference, visualization, and more.""" +A broader [ecosystem](/packages/#ecosystem) of packages builds on the scverse core, extending it with domain-specific models and analysis methods.""" # Team [team] text = """\ -Scverse is a community project currently governed by the developers of the core packages. Please [reach out](/join) if you'd like to be involved! +Scverse is a community project currently governed by a core team. Please [reach out](/join) if you'd like to be involved! """ # People information is recorded in the people/_index.md file @@ -29,7 +97,7 @@ Scverse is a community project currently governed by the developers of the core [references] text = """\ -scverse tools are used in research and industry projects across the globe and are cited in [thousands](https://scholar.google.ru/scholar?cites=14568046068402025757) of academic publications. +scverse tools are used in research and industry across the globe. If they are useful in your work, please cite [the scverse paper](https://www.nature.com/articles/s41587-023-01733-8) along with the individual packages you used. """ diff --git a/layouts/index.html b/layouts/index.html index c5be35d8..c9e95bca 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -63,78 +63,91 @@ - {{ with first 1 (where (where .Site.RegularPages "Section" "events") "Date.Unix" ">" now.Unix).ByDate }} -
-
Next event
-
- {{ range . }} - -
-
- {{ .Date.Format "January 02, 2006" }} -
-
-

{{ .Title }}

- {{ .Description }} -
-
-
+ {{ with .Params.features }} +
+
What every core package provides
+
+ {{ range .items }} +
+
+
+
{{ .title }}
+

{{ .text }}

+
+
{{ end }} - -
See all scverse events
-
{{ end }} - {{ partial "main/packages" . }} +
+
+
+
+

Core packages

+

+ {{ .Params.packages.text | markdownify }} +

+ +
Browse the core packages
+
+
-
-
Ecosystem
-
-

- {{ .Params.ecosystem.text | markdownify }} -

- -
Continue to scverse ecosystem
-
-
-
+
+
+

Ecosystem

+

+ {{ .Params.ecosystem.text | markdownify }} +

+ +
Continue to scverse ecosystem
+
+
-
-
Mission
-
-

- {{ .Params.mission.text | markdownify }} -

- -
Read more about scverse
-
-
-
+
+
+

Events

+

+ {{ .Params.events.text | markdownify }} +

+ +
See all scverse events
+
+
-
-
Team
-
-

- {{ .Params.team.text | markdownify }} -

- -
Learn more about scverse community
-
-
-
+
+
+

Mission

+

+ {{ .Params.mission.text | markdownify }} +

+ +
Read more about scverse
+
+
+ +
+
+

Team

+

+ {{ .Params.team.text | markdownify }} +

+ +
Learn more about scverse community
+
+
-
-
References
-
-

- {{ .Params.references.text | markdownify }} -

- -
Find each package's publication
-
+
+
+

References

+

+ {{ .Params.references.text | markdownify }} +

+ +
Find each package's publication
+
+
diff --git a/layouts/partials/header.html b/layouts/partials/header.html index fc8ce556..138e8b38 100644 --- a/layouts/partials/header.html +++ b/layouts/partials/header.html @@ -29,6 +29,7 @@ href="/packages" id="packages-dropdown" role="button" + data-bs-toggle="dropdown" aria-expanded="false" > Packages @@ -77,6 +78,7 @@ href="/events" id="events-dropdown" role="button" + data-bs-toggle="dropdown" aria-expanded="false" > Events @@ -107,6 +109,7 @@ href="/about" id="about-dropdown" role="button" + data-bs-toggle="dropdown" aria-expanded="false" > About @@ -139,6 +142,33 @@ +
- + {{ with .Params.hero }} +

{{ .text | markdownify }}

+
+ {{ range $i, $p := .points }} + +
+
{{ $p.title }}
+

{{ $p.text }}

+
+ {{ end }} +
+ {{ end }} diff --git a/layouts/partials/main/packages.html b/layouts/partials/main/packages.html deleted file mode 100644 index fc1794fb..00000000 --- a/layouts/partials/main/packages.html +++ /dev/null @@ -1,38 +0,0 @@ -
-
Core packages
-
- {{ $registry := partialCached "registry.html" "registry" }} - {{ with $registry.core }} -
- {{ range $p := . }} -
-
-
- {{ with .logo }} - Logo for {{ $p.name }} - {{ end }} -
-
- -
- {{ .name }} -
-

- {{ .blurb }} -

-
-
-
- {{ end }} -
- {{ end }} - -
View all scverse packages
-
-
-
diff --git a/static/img/icons/xcom.svg b/static/img/icons/xcom.svg new file mode 100644 index 00000000..ed2d7645 --- /dev/null +++ b/static/img/icons/xcom.svg @@ -0,0 +1 @@ + diff --git a/static/main.js b/static/main.js index f2cc3a27..1ed2153c 100644 --- a/static/main.js +++ b/static/main.js @@ -267,16 +267,16 @@ const initInteractiveViz = () => { // Exit early if visualization elements don't exist on this page if (!visualization || !card) return; - // Color clusters for UMAP visualization + // Color clusters for UMAP visualization, using the same brand hues the package tiles below use instead of a generic chart-library palette. const colorClusters = [ - { color: '#4285F4', count: 14, name: 'Cluster A' }, - { color: '#34A853', count: 12, name: 'Cluster B' }, - { color: '#FBBC05', count: 10, name: 'Cluster C' }, - { color: '#EA4335', count: 8, name: 'Cluster D' }, - { color: '#FF00FF', count: 11, name: 'Cluster E' }, - { color: '#00BCD4', count: 9, name: 'Cluster F' }, - { color: '#9C27B0', count: 7, name: 'Cluster G' }, - { color: '#FF9800', count: 13, name: 'Cluster H' } + { color: '#40a9ff', count: 68, name: 'Cluster A' }, + { color: '#4ab274', count: 58, name: 'Cluster B' }, + { color: '#fbb822', count: 52, name: 'Cluster C' }, + { color: '#e5864b', count: 44, name: 'Cluster D' }, + { color: '#da347f', count: 54, name: 'Cluster E' }, + { color: '#969dea', count: 48, name: 'Cluster F' }, + { color: '#de367b', count: 38, name: 'Cluster G' }, + { color: '#6cf1a1', count: 62, name: 'Cluster H' } ]; // 3D tilt effect @@ -329,7 +329,7 @@ const initInteractiveViz = () => { dot.dataset.cluster = cluster.name; dot.dataset.color = cluster.color; - const size = Math.floor(Math.random() * 12) + 8; + const size = Math.floor(Math.random() * 8) + 5; dot.style.width = `${size}px`; dot.style.height = `${size}px`; @@ -341,7 +341,7 @@ const initInteractiveViz = () => { u = u / 6 - 0.5; v = v / 6 - 0.5; - const distance = Math.random() * 70 + 10; + const distance = Math.random() * 130 + 20; const dx = u * distance * 2; const dy = v * distance * 2; const x = centerX + dx; @@ -362,7 +362,7 @@ const initInteractiveViz = () => { setTimeout(() => { dot.style.transform = 'scale(1)'; dot.style.opacity = '1'; - }, i * 50 + Math.random() * 200); + }, i * 18 + Math.random() * 150); } });