Skip to content

Commit

Permalink
2022 Redesign (#188)
Browse files Browse the repository at this point in the history
* added updating servers_count to filters in stable lists (items with 0 still shown)

pretty complicated setup, but i think works well

* fixed footer swoosh getting clipped, re-exported swoosh to make its edges tighter to the footer bounding box

* wrapping apps categories on mobile

* move SponsorGroup to a separate component file

* render out platinum + gold sponsors

* rm errant coma, add `light: true` for logos that need to be inverted

* add silver sponsors

* add `SponsorCard` component

* add link to silver sponsors

* add hero copy

* add sponsorship info sections

* extract sponsor strings

* rename `SponsorGroup` => `SponsorLogoGroup`

* updating iconCard styling to use a white bg

* rm redundant margin

* add rounded corners to default sponsor icon

* adding more api types

* using unicode-bidi:plaintext on potentially RTL user-supplied content

* adding language dropdown to servers filters

* linked homepage hero to servers

* add bg illo

* more large buttons on the homepage #48

* using `medium` button size for `large` buttons on mobile #48

* fix bug where the illo is too tall and repeats

* updating homepage hero CTAs

leaving the get_started string at that id because localizations aren't necessarily going to have the repetition with get the app that english does, and there's a lot of translations for it already

via https://www.figma.com/file/Wk8L4YNZvgTBcrpthCYmBj?node-id=320:1244#235296710

* use updated illo

* rename illo

* removing commented-out server-side code. thinking servers should be lazy loaded because they're big for an initial page load and might hold up FCP; and the server filtering should be implemented upstream in api.joinmastodon.org

* adding caching ONLY for categories, added all topics option, removed server count from language dropdown, using accessible checkboxes for filters

* updated server card design, localized number formatter

* i added rounded too many times. just needed to update it on the image

* removed sass-specific code and using tailwind breakpoints in globals to reduce redundancy

* removing old badges

* right-aligning AppCard text when RTL

* using large buttons on app features

* extracting apps grid into its own file

* try adding homepage hero

* reduce `SponsorLogoGroup` size, add hover transition

closes #46

* add server cards

* oops fix duplicate keys

* sorting apps, removing prettier where we're defining data because it's just.... so verbose and irregular

* move cards to the bottom if this is a subsequent visit

* fixing some typescript issues in apps grid, making released_on a real date

* fix duplicate message key

* add /server link to header

* refactoring twoupfeature (name?) out of the apps page

* update home page hero asset

* adjust height on smaller screen sizes

* rm unused css class

* use `b2` on testimonial text

closes #47

* moved apps data to its own file, typed and documented structure

* update /sponsors to use `TwoUpFeature` component

* add margin beneath `SponsorLogoGroup`

* wrap server filter line on mobile

* filter out languages that don’t have a label or locale

handles edge case for now where “cz” does not have a label

* adjust breakpoint for server filters/list being stacked

* added old imprint page

* updated imprint for next, with some additional semantics

* added a basic page wrapper component for imprint, etc that don't have designs and are just basic text

* added basic mobile nav

* styled mobile menu a bit, fixed some focus-related menu behavior in safari

* use new homepage hero assets

* use next image

* try using blurred placeholders

* linking footer "Get started" to /servers, adding a redirect from /communities for backwards compatability

* removing an old todo

* add priority to hero images

* add mobile server hero asset

* add mobile and desktop hero illos

* add hero text

* use rich text formatting for body copy, adjust positioning

* added loading state for server cards

* adjust width of body copy

* limiting amount of skeleton styling, using CH units instead of EMs

(they're more or less the same, but characters are easier to count + reason about than root EMs along the x axis

* center image

* get height from image import instead of hardcoding it

* added some basic misc documentation

* framing appHero correctly on /apps

* set hero heights as css variables

forgot that these height classes won’t be generated when tailwind compiles

* bulk documentation and cleanup

* sponsor grid updates for less explicit columns, extracting out cards

* typescript updates

* using general as the default filter to match current site, prevent page from loading 100+ servers on load

* going to two grid cols a little earlier on /servers

* added focus-visible-within selector ("variant"??)

* adding hocus selector, adding more focus styles

* containing reordered server sections to reduce the number of indexes counted. putting filter heading up top next to the serverFilters. spacing out GettingStartedCards and the grid section

* using icons from figma for GettingStartedCards

* added recovertranslations script to package.json and ran extract

* using jsdoc where comments defines descriptions on single variables

* marking linkAttr attributes as optional

* using plain string props if there's no interpolation that needs template strings

* added privacy-policy, updated imprint

* moved hero into its own file, added variants for homepage/inner pages, default illustration, etc etc

illos should still have 2x renderings later, but the code scaffolding should support dropping them in when that happens

* add carousel for testimonials

* try adding carousel to “Why Mastodon?” section

* Revert "try adding carousel to “Why Mastodon?” section"

This reverts commit 1f32e19.

* updated features_ images on homepage from drive

fixes #54

* add missing key

* add missing key

* add favicons

* tightened h1 leading #56, made spacing more even on homepage hero

* update testimonials card

* use grid instead of carousel

* add target/rel to link

* add missing key

* reduce max height for logos, update text style

closes #46

* rm `mix-blend-luminosity` on hover/focus

* removing unused global styles

* h2: 1.1 leading

fixes #56

* /apps hero, illos pending

* updating spacing on features to match figma via discord, using sh1 for copy

* removing carousel deps for now

* added wide apps hero

* add `cursor-pointer` to filter buttons

* only use giant size on 2xl (option 1)

* bump elephant over on tablet size

* reimplementing mika's server-side ordering of categories by their servers_count

this puts general at the top of the real list, since it's the default, and prevents the order from changing when other filters change

* padding the /servers category filters to match the mockup more closely

* white-bg nav dropdowns

* added active styling to nav items and language selector. lightened mobile menu weights accordingly

fixes #63

* adding a couple of aria-current attrs for #63

* slightly better (wrapped) server categories on mobile. could still use some disclosure ui to hide/show

* reduce max height on logos, fit on 5 lines

* add links to feature section buttons

* randomize testimonials and show first 6

* increase line height, show last 6 testimonials

closes #47

* mirror hero image when text is rtl

closes #64

* update locale files

* using more traditional badges, adjusting their layout

fixes #60

* adjust padding on `TwoUpFeature` on sponsors page

* load translations, rel #30

* closing mobile takeover menu when clicking menu items (not dropdown buttons)

fixes #59

* updated button hover state from figma and added buttons to the /guide

* used values from tailwind config in the guide to improve maintainability of the guide a bit

* adding gap to icons section of the guide

* scoped hero changed to the homepage

* added drop shadow behind text in heros

* removing overflow:auto when dropdown closed,

also removing max-height on mobile submenus, since that makes a nested scroll context

rel #65

* using margin-inline-start instead of margin-left for better RTL support on homepage hero

fixes #74

* adding translation string props to /servers, noting which pages don't need them

* update copy, closes #69

* fix duplicated ID warnings

* center items on TwoUpFeature, closes #73

* use b2, closes #47

* add meta title and description to the homepage, rel #75

* increase border radius on avatars, rel #72

* add sponsors page description, rel #75

* edit text alignment on centered TwoUpFeature, rel #73

* add og:image, rel #75

* fix key warning

* og:image needs to be an absolute url, move illo

* add carousel

* add breakpoint for number of slides shown

* trying to figure out rtl

* keep carousel ltr

* re-org file so components are listed in order of use

* add carousel to WhyMastodon section

* edit spacing, hide shadow on mobile

* fixing our react implementation of the <select> menu

* giving select menus pointer cursors and gray bg on hover

* Add hover state to navbar dropdown menus

Fixes #70

* add cache header, rel #66

* move loadIntlMessages to getStaticProps

these messages only change when the repo is updated, so these can just be loaded on build

* nvm, can’t mix ssg and getstaticprops!!

Didn’t realize you couldn’t have both. This reverts commit 37e8941.

* realizing the header offset on mobile is there for the safe-area, but that would be a pain to reliably implement

making the offset 0 on mobile for now so it's not offset on load

* touch ups for header nav updates

* black bg on mobile menu?

* fixing submenu weight

* adjustments for black-bg submenus

* hotfix for the un-scrolled state of the header's backdrop blur, which was a fully transparent blur

rel #70

* load messages

* update category list to be fetched client side only

* restore stable category list, clean up code

* clean up comments

* clean up unused imports

* add skelton text

* placeholder link to test on preview

* rm test link

* pull from query cache

* show prev server count when the new data is loading

* removed spatial keyboard navigation

could be reimplemented with https://open-ui.org/components/focusgroup.explainer when it lands, but maintaining the functionality from scratch is a bit too involved

* tightened above-the-fold space on /apps

* setting node version suggestion because of GoogleChromeLabs/squoosh#1242

* removed filters label from /servers

fixes #68

* arranged tailwind grayscales to match gradient order in :root

* adding sponsor.name as logo img's alt

was trying this out with a screen reader and think "link, image, oak studios" is clear, succinct, and scannable rather than saying having a sea of `Logo of company X` labels

* adding descriptive label to menu button

* adding descriptive label to 文A, which i think is communicative visually, but on its own, less so aurally

notably, the comma helps the screenreader pause so it doesn't sound like "文—A translated site" rather than "文A—Translate site"

* documentation update

* adding back the no_results text on /servers

* making error response text translateable

* removing featured servers logic until the data is available

* added alt text on /apps screenshots

* lighthouse thinks footer headings should be h2s—makes sense.

* move data transforms to `select()`, don’t use query cache

* rm comment

* show previous data while the new data is loading

* keep the initial order

* safelisting type/bgColor classes for /guide

* moving header/footer's `nav.__` ids to `nav.__.title` to harmonize nav structure and allow for other properties like description,

ran extract/recover again

* realizing that even though imprint/privacy policy don't need translations for content, they still need to loadIntlMessasges for the navs. also making the content dir=ltr so that reading flows more clearly

* expanding /guide with more component examples, better wrapper styling

* adding old <title> for /servers

* updating sponsors data from upstream (49e398, 6d9faa)

* deleting z_archive

rel oakstudios#29

* removing ignoreBuildErrors from next config. build does seem fine locally

* adding existing upstream publish github actions workflow

* bumped all deps

* accessing arbitrary named properties on resolved tailwind config with bracket notation so that the keys' types match the config typings

(fixing broken build after dep update)

* rm recoverTranslations file,now that we’re done with the archive

Co-authored-by: Mika Busante <mika@mikabusante.com>
Co-authored-by: Mika Busante <31963784+mikabusante@users.noreply.github.com>
Co-authored-by: Skylar Challand <skylar@oakmade.com>
  • Loading branch information
4 people committed Aug 26, 2022
1 parent 6d9faa5 commit 15ea98d
Show file tree
Hide file tree
Showing 504 changed files with 7,760 additions and 24,723 deletions.
12 changes: 0 additions & 12 deletions .babelrc

This file was deleted.

5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
tab_width = 2
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = false
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "prettier"]
}
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
SOURCE: ./build/
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.REMOTE_TARGET }}
TARGET: ${{ secrets.REMOTE_TARGET }}
22 changes: 16 additions & 6 deletions .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
.env
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# no CSSerino
src/**/*.css
!src/fonts/fonts.css
!src/fonts/ionicons.min.css
# vercel
.vercel
19 changes: 0 additions & 19 deletions .gitlab-ci.yml

This file was deleted.

1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
16.15.1
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
z_archive
node_modules
.next
.swc
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": false
}
53 changes: 50 additions & 3 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
# joinmastodon.org

[![pipeline status](https://source.joinmastodon.org/mastodon/joinmastodon/badges/master/pipeline.svg)](https://source.joinmastodon.org/mastodon/joinmastodon/commits/master)
Informational site for the Mastodon project

Informational page for the Mastodon project, based on React.js, created with create-react-app.
Submit translations here: https://crowdin.com/project/joinmastodon

**Submit translations here:** <https://crowdin.com/project/joinmastodon>
## Development

### Installation

```sh
yarn
yarn run dev
```

See `/package.json` for more scripts.

### Built with

- [Next.js](https://nextjs.org/)
- [Tailwind.css](https://tailwindcss.com/)
- [React Query](https://tanstack.com/query/v4/docs/adapters/react-query)
- [React Intl](https://formatjs.io/docs/react-intl/)

### Translations

Submit translations at [Crowdin](https://crowdin.com/project/joinmastodon).

Translations are managed via Crowdin and distributed with react-intl. The basic translation workflow is:

1. Messages are declared using the `<FormattedMessage>` component
2. Default messages are extracted to `locales/en.json` by running `yarn extract`
3. The locale files are synced with the Crowdin project. Any new messages in the source file(`locales/en.json`) are made available for translation for other languages. Any new translated messages are downloaded back to the repo into corresponding locale files (e.g. `locales/es.json`).
4. On build, each locale loads its corresponding messages from `getStaticProps()` ([example](https://github.com/oakstudios/joinmastodon/blob/461b65b7ef57576b6d74ef5ee0e34521d7e81b09/pages/index.js#L309-L313)).

#### Internationalized Routing

Localized versions of each page are hosted at dedicated URLs, making it easier for web crawling in users' preferred language. ([docs](https://nextjs.org/docs/advanced-features/i18n-routing))

### Image optimization

Raster images should be added at the maximum resolution they'll be displayed at. If art direction is not required, mobile images will be generated automatically. ([docs](https://nextjs.org/docs/basic-features/image-optimization))

## Deployment

Next.js should be hosted using a Node.js server. ([see instructions](https://nextjs.org/docs/deployment#self-hosting)). While it absolutely can exported to static HTML,

- [image optimization](#image-optimization) (for automatic webp conversion and compression)
- [internationalized routing](#internationalized-routing) ([code reference](https://github.com/oakstudios/joinmastodon/blob/87a3c1df9dce50141e097f26ebd1483b0c1bce4a/next.config.js#L9-L12))
- and redirects ([code reference](https://github.com/oakstudios/joinmastodon/blob/87a3c1df9dce50141e097f26ebd1483b0c1bce4a/next.config.js#L16-L24))

are the primary benefits of a Node.js server for this project.

For now, Node 16 LTS is recommended for building the site until support for Node 18 is provided by [Squoosh](https://github.com/GoogleChromeLabs/squoosh/issues/1242).
39 changes: 39 additions & 0 deletions components/AppCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import classnames from "classnames"
import Image from "next/image"
import SVG from "react-inlinesvg"
import { FormattedMessage } from "react-intl"

export type AppCardProps = {
name: React.ReactNode
icon: string
url: URL
paid: boolean
}

/**
* Renders a card with app data.
* Layout (width, height, positioning) can be set from the parent.
*/
export const AppCard = ({ name, icon, url, paid }) => {
return (
<a href={url} target="_blank" rel="noopener noreferrer">
<div className="flex items-stretch justify-start gap-4 rounded bg-white p-2 shadow md:p-4">
<div className="h-[3.5rem] w-[3.5rem] flex-shrink-0 overflow-hidden rounded-sm">
<Image src={icon} alt={`Logo for ${name}`} />
</div>
<div className="flex flex-auto flex-col">
<span className="b4 block text-gray-1">
{paid ? (
<FormattedMessage id="apps.paid" defaultMessage="Paid" />
) : (
<FormattedMessage id="apps.free" defaultMessage="Free" />
)}
</span>
<h3 className="b1 flex flex-auto items-center !font-700 !leading-[1] rtl:text-right">
<span dir="ltr">{name}</span>
</h3>
</div>
</div>
</a>
)
}
59 changes: 59 additions & 0 deletions components/AppHero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Image, { ImageProps } from "next/image"
import { FormattedMessage } from "react-intl"

import downloadOnGooglePlay from "../public/badges/google-play.svg"
import downloadOnAppStore from "../public/badges/app-store.svg"

export type AppHeroProps = {
/** Image source value passed to `next/image`'s `src` */
backgroundImage: ImageProps["src"]
/** Image-framing value passed to `next/image`'s `object-position` */
backgroundImagePosition?: string
}
/** Renders a hero with links to the app store, typically at the bottom of a page. */
export const AppHero = ({
backgroundImage,
backgroundImagePosition = "center center",
}: AppHeroProps) => {
return (
<section className="full-width-bg relative -mb-footer-offset pb-footer-offset pt-32">
<div className="absolute inset-0 -z-10">
<Image
src={backgroundImage}
alt=""
layout="fill"
objectFit="cover"
objectPosition={backgroundImagePosition}
/>
</div>
<div className="full-width-bg__inner flex flex-col gap-12 pb-[50vw] md:gap-20">
<h2 className="h1 text-center">
<FormattedMessage
id="browse_apps.get_started"
defaultMessage="Get started today"
/>
</h2>

<div className="grid-cols-12 justify-center gap-gutter md:grid">
<div className="col-span-6 col-start-4 mx-auto grid max-w-xs grid-cols-2 justify-center gap-gutter md:mx-0 md:max-w-none md:justify-start xl:col-span-4 xl:col-start-5">
<a href="https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974">
<Image
src={downloadOnAppStore}
layout="responsive"
alt="Download on the App Store"
/>
</a>
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android">
<Image
src={downloadOnGooglePlay}
layout="responsive"
alt="Get it on Google Play"
/>
</a>
</div>
</div>
</div>
</section>
)
}
export default AppHero
106 changes: 106 additions & 0 deletions components/AppsGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { FormattedMessage, useIntl } from "react-intl"
import { AppCard } from "../components/AppCard"
import classNames from "classnames"
import { useState } from "react"
import SelectMenu from "../components/SelectMenu"
import { sortBy as _sortBy } from "lodash"
import type { appsList } from "../data/apps"

export type AppsGridProps = {
apps: appsList
}

/** Renders AppCards as a grid, with sorting and filtering options */
export const AppsGrid = ({ apps }: AppsGridProps) => {
const intl = useIntl()
const [activeCategory, setActiveCategory] = useState("all")

/** normalizing the apps dictionary as an array */
const allApps = Object.entries(apps)
.map(([category, apps]) =>
apps.map(({ name, icon, url, paid, released_on }) => ({
name,
icon,
url,
category,
paid: paid ?? false,
released_on: new Date(released_on) ?? null,
}))
)
.flat()

//prettier-ignore
const sortOptions = [
{ value: "date_added", label: intl.formatMessage({ id: "sorting.recently_added", defaultMessage: "Recently Added" }) },
{ value: "paid", label: intl.formatMessage({ id: "sorting.free", defaultMessage: "Free" }) },
{ value: "name", label: intl.formatMessage({ id: "sorting.name", defaultMessage: "Alphabetical" }) },
]
const [sortOption, setSortOption] = useState(sortOptions[0].value)
const filteredApps = allApps.filter(
({ category }) => category === activeCategory || activeCategory === "all"
)
const sortedAndFilteredApps = _sortBy(filteredApps, sortOption)

//prettier-ignore
const categories = [
{ key: "all", label: intl.formatMessage({ id: "browse_apps.all", defaultMessage: "All" }) },
{ key: "android", label: intl.formatMessage({ id: "browse_apps.android", defaultMessage: "Android" }) },
{ key: "ios", label: intl.formatMessage({ id: "browse_apps.ios", defaultMessage: "iOS" }) },
{ key: "web", label: intl.formatMessage({ id: "browse_apps.web", defaultMessage: "Web" }) },
{ key: "sailfish", label: intl.formatMessage({ id: "browse_apps.sailfish", defaultMessage: "SailfishOS" }) },
{ key: "desktop", label: intl.formatMessage({ id: "browse_apps.desktop", defaultMessage: "Desktop" }) },
]
return (
<div>
<div>
<h2 className="h4 mb-8">
<FormattedMessage
id="browse_apps.title2"
defaultMessage="Browse third-party apps"
/>
</h2>
<div className="-mx-gutter pis-gutter mb-6 overflow-x-auto">
<div className="flex flex-wrap gap-gutter md:flex-nowrap">
{categories.map((category) => (
<label
key={category.key}
className={classNames(
"b3 block cursor-pointer whitespace-nowrap rounded border-2 p-4 text-center !font-600 transition-all md:w-full",
category.key === activeCategory
? "border-accent-blurple bg-accent-blurple text-white hover:border-dark-blurple hover:bg-dark-blurple focus-visible-within:border-dark-blurple focus-visible-within:bg-dark-blurple"
: "border-accent-blurple bg-white text-accent-blurple hover:border-dark-blurple hover:text-dark-blurple"
)}
>
<input
className="sr-only"
type="radio"
name="apps-selection"
id=""
value={category.key}
onChange={(e) => setActiveCategory(e.target.value)}
/>
{category.label}
</label>
))}
</div>
</div>
</div>
<div className="my-8">
<SelectMenu
label={
<FormattedMessage id="sorting.sort_by" defaultMessage="Sort" />
}
value="all"
onChange={(v) => {
setSortOption(v)
}}
options={sortOptions}
/>
</div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-4">
{sortedAndFilteredApps.map(AppCard)}
</div>
</div>
)
}
export default AppsGrid
16 changes: 16 additions & 0 deletions components/BasicPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Layout scaffolding for page files that need an 8-column
* area for rich text, but no additional sections.
* (eg: /imprint)
*/
export const BasicPage = ({ children }) => {
return (
<div className="grid-cols-12 pt-[var(--header-area)] md:grid">
<div className="pt-16 md:col-span-10 md:col-start-2 lg:col-span-8 lg:col-start-3">
{children}
</div>
</div>
)
}

export default BasicPage
Loading

0 comments on commit 15ea98d

Please sign in to comment.