Skip to content

Commit

Permalink
Introduce StyleX for styling (#411)
Browse files Browse the repository at this point in the history
* wip

* remove declaration override

* append stylex styles

* fix things

* fix bottom margin

* remove dev runtime

* revert thing

* revert

* use stylex for keyframe animations

* pass logo url via function

* update stylex version

* Fix generated CSS containing a bunch of `:not(#\#)`s.

See [this issue](facebook/stylex#370) for more info.

* add eslint plugin to validate stylex styles

* use numbers that default to px

* use numbers more
  • Loading branch information
vinceau authored Jan 24, 2024
1 parent 1e0eef0 commit 9a08952
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 86 deletions.
6 changes: 0 additions & 6 deletions .babelrc

This file was deleted.

15 changes: 13 additions & 2 deletions .erb/configs/webpack.config.renderer.dev.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "webpack-dev-server";

import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
import StylexPlugin from "@stylexjs/webpack-plugin";
import chalk from "chalk";
import { execSync, spawn } from "child_process";
import fs from "fs";
Expand All @@ -14,9 +15,11 @@ import baseConfig from "./webpack.config.base";
import polyfills from "./webpack.config.renderer.polyfills";
import webpackPaths from "./webpack.paths";

const isDevelopment = process.env.NODE_ENV !== "production";

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === "production") {
if (!isDevelopment) {
checkNodeEnv("development");
}

Expand Down Expand Up @@ -187,9 +190,17 @@ export default (env?: Record<string, string | true>, _argv?: any) => {
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== "production",
isDevelopment,
nodeModules: webpackPaths.appNodeModulesPath,
}),

new StylexPlugin({
dev: isDevelopment,
unstable_moduleResolution: {
type: "commonJS",
rootDir: webpackPaths.rootPath,
},
}),
],

node: {
Expand Down
15 changes: 14 additions & 1 deletion .erb/configs/webpack.config.renderer.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Build config for electron renderer process
*/

import StylexPlugin from "@stylexjs/webpack-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
Expand All @@ -20,6 +21,8 @@ import webpackPaths from "./webpack.paths";
checkNodeEnv("production");
deleteSourceMaps();

const isDevelopment = process.env.NODE_ENV !== "production";

const devtoolsConfig =
process.env.DEBUG_PROD === "true"
? {
Expand Down Expand Up @@ -115,6 +118,16 @@ const configuration: webpack.Configuration = {
filename: "style.css",
}),

new StylexPlugin({
dev: isDevelopment,
unstable_moduleResolution: {
type: "commonJS",
rootDir: webpackPaths.rootPath,
},
useCSSLayers: true,
appendTo: "style.css",
}),

new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === "true" ? "server" : "disabled",
}),
Expand All @@ -128,7 +141,7 @@ const configuration: webpack.Configuration = {
removeComments: true,
},
isBrowser: false,
isDevelopment: process.env.NODE_ENV !== "production",
isDevelopment,
}),

new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }),
Expand Down
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
tsconfigRootDir: __dirname,
createDefaultProgram: true,
},
plugins: ["simple-import-sort", "strict-booleans", "react-hooks", "prettier"],
plugins: ["simple-import-sort", "strict-booleans", "react-hooks", "prettier", "@stylexjs"],
extends: [
"plugin:react/recommended",
"eslint:recommended",
Expand Down Expand Up @@ -100,6 +100,7 @@ module.exports = {
"react/react-in-jsx-scope": "off",
"react/jsx-boolean-value": ["error", "always"],
"react/no-unstable-nested-components": "error",
"@stylexjs/valid-styles": "error",
},
ignorePatterns: ["/*.js", "node_modules"],
};
29 changes: 29 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import styleXPlugin from '@stylexjs/babel-plugin';

const config = {
// presets: [
// "@babel/preset-env",
// "@babel/preset-typescript"
// ],
plugins: [
[
styleXPlugin,
{
dev: true,
// Set this to true for snapshot testing
// default: false
test: false,
// Required for CSS variable support
unstable_moduleResolution: {
// type: 'commonJS' | 'haste'
// default: 'commonJS'
type: 'commonJS',
// The absolute path to the root directory of your project
rootDir: __dirname,
},
},
],
],
};

export default config;
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
"@storybook/preset-scss": "^1.0.3",
"@storybook/react": "^6.5.10",
"@storybook/testing-library": "^0.0.13",
"@stylexjs/eslint-plugin": "^0.4.1",
"@stylexjs/webpack-plugin": "^0.4.1",
"@svgr/webpack": "^6.1.2",
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"@testing-library/jest-dom": "^5.16.1",
Expand Down Expand Up @@ -161,6 +163,7 @@
"@mui/lab": "^5.0.0-alpha.127",
"@mui/material": "^5.12.1",
"@slippi/slippi-js": "^6.7.0",
"@stylexjs/stylex": "^0.4.1",
"@xmcl/nat-api": "^0.4.1",
"async-mutex": "^0.4.0",
"compare-func": "^2.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
} as ComponentMeta<typeof BouncingSlippiLogo>;

// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<typeof BouncingSlippiLogo> = (args) => <BouncingSlippiLogo {...args} />;
const Template: ComponentStory<typeof BouncingSlippiLogo> = () => <BouncingSlippiLogo />;

export const Primary = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import { css, keyframes } from "@emotion/react";
import * as stylex from "@stylexjs/stylex";
import React from "react";

import slippiLogo from "@/styles/images/slippi_logo.svg";

const bounceAnimation = keyframes`
0% { bottom: 0px; }
100% { bottom: 25px; }
`;
const bounceAnimation = stylex.keyframes({
"0%": { bottom: 0 },
"100%": { bottom: 25 },
});

const barrelRollAnimation = keyframes`
0% { transform: rotate(0); }
100% { transform: rotate(720deg); }
`;
const barrelRollAnimation = stylex.keyframes({
"0%": { transform: "rotate(0)" },
"100%": { transform: "rotate(720deg)" },
});

const onlyBounce = css`
animation: ${bounceAnimation} 0.6s infinite alternate;
`;
const styles = stylex.create({
container: {
display: "flex",
position: "relative",
paddingTop: 20,
height: 80,
width: 80,
},
logo: (logoUrl: string) => ({
backgroundImage: `url("${logoUrl}")`,
backgroundSize: "contain",
backgroundRepeat: "no-repeat",
position: "absolute",
height: 60,
width: 80,
}),
onlyBounce: {
// eslint-disable-next-line @stylexjs/valid-styles
animation: `${bounceAnimation} 0.6s infinite alternate`,
},
bouncePlusSpin: {
// eslint-disable-next-line @stylexjs/valid-styles
animation: `${bounceAnimation} 0.6s infinite alternate, ${barrelRollAnimation} 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) alternate forwards`,
},
});

const bouncePlusSpin = css`
animation: ${bounceAnimation} 0.6s infinite alternate,
${barrelRollAnimation} 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) alternate forwards;
`;

export const BouncingSlippiLogo = ({ size = "80px" }: { size?: string }) => {
export const BouncingSlippiLogo = () => {
const ref = React.createRef<HTMLDivElement>();
const [animationState, setAnimationState] = React.useState<"running" | "ready">("ready");

Expand All @@ -46,25 +63,12 @@ export const BouncingSlippiLogo = ({ size = "80px" }: { size?: string }) => {
}, [animationState, setAnimationState]);

return (
<div
css={css`
display: flex;
position: relative;
padding-top: 20px;
height: ${size};
width: ${size};
`}
>
<div {...stylex.props(styles.container)}>
<div
css={css`
background-image: url("${slippiLogo}");
background-size: contain;
background-repeat: no-repeat;
${animationState === "ready" ? onlyBounce : bouncePlusSpin}
position: absolute;
height: calc(${size}*0.75);
width: ${size};
`}
{...stylex.props(
styles.logo(slippiLogo),
animationState === "ready" ? styles.onlyBounce : styles.bouncePlusSpin,
)}
ref={ref}
onMouseOver={onMouseOver}
/>
Expand Down
61 changes: 26 additions & 35 deletions src/renderer/pages/home/news_feed/news_article/news_article.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
import type { NewsItem } from "@common/types";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import CardActions from "@mui/material/CardActions";
import CardContent from "@mui/material/CardContent";
import CardMedia from "@mui/material/CardMedia";
import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography";
import stylex from "@stylexjs/stylex";
import moment from "moment";
import React from "react";
import TimeAgo from "react-timeago";

import { ExternalLink } from "@/components/external_link";
import { MarkdownContent } from "@/components/markdown_content";

const styles = stylex.create({
container: {
marginBottom: 20,
},
dateInfo: {
marginRight: "auto",
marginLeft: 5,
opacity: 0.6,
fontSize: 15,
},
fixedCardHeight: {
height: 200,
},
markdownContainer: {
color: "#ccc",
maxWidth: 700,
},
});

export const NewsArticle = React.memo(function NewsArticle({ item }: { item: NewsItem }) {
const { imageUrl, title, subtitle, permalink, body, publishedAt } = item;
const localDateString = moment(publishedAt).format("LLL");

return (
<Outer>
<div {...stylex.props(styles.container)}>
<Card>
{imageUrl && (
<CardMedia
css={css`
height: 200px;
`}
image={imageUrl}
title={title}
/>
)}
{imageUrl && <CardMedia {...stylex.props(styles.fixedCardHeight)} image={imageUrl} title={title} />}
<CardContent>
<Typography gutterBottom={true} variant="h5" component="h2">
{title}
Expand All @@ -40,38 +50,19 @@ export const NewsArticle = React.memo(function NewsArticle({ item }: { item: New
{subtitle}
</Typography>
)}
{body && (
<MarkdownContent
content={body}
css={css`
color: #ccc;
max-width: 700px;
`}
/>
)}
{body && <MarkdownContent content={body} {...stylex.props(styles.markdownContainer)} />}
</CardContent>
<CardActions disableSpacing={true}>
<Tooltip title={localDateString}>
<DateInfo>
<div {...stylex.props(styles.dateInfo)}>
Posted <TimeAgo date={publishedAt} title="" live={false} />
</DateInfo>
</div>
</Tooltip>
<Button LinkComponent={ExternalLink} size="small" color="primary" href={permalink}>
Read more
</Button>
</CardActions>
</Card>
</Outer>
</div>
);
});

const Outer = styled.div`
margin-bottom: 20px;
`;

const DateInfo = styled.div`
margin-right: auto;
margin-left: 5px;
opacity: 0.6;
font-size: 15px;
`;
Loading

0 comments on commit 9a08952

Please sign in to comment.