1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

frontend: start using CSS modules for components with design tokens

This commit is contained in:
Quentin Gliech
2023-08-03 18:28:43 +02:00
parent 7c2e691175
commit c8ba2a1fa3
17 changed files with 229 additions and 102 deletions

View File

@ -0,0 +1,21 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.block {
background-color: var(--cpd-color-bg-subtle-secondary);
color: var(--cpd-color-text-primary);
padding: var(--cpd-space-4x);
border-radius: var(--cpd-space-2x);
}

View File

@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import styles from "./Block.module.css";
type Props = {
children: React.ReactNode;
highlight?: boolean;
className?: string;
};
const Block: React.FC<Props> = ({ children, highlight, className }) => {
const Block: React.FC<React.PropsWithChildren<Props>> = ({
children,
highlight,
}) => {
return (
<div className={className} data-active={highlight}>
<div className={styles.block} data-active={highlight}>
{children}
</div>
);

View File

@ -0,0 +1,21 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.block-list {
display: grid;
grid-template-columns: 1fr;
gap: var(--cpd-space-4x);
align-content: flex-start;
}

View File

@ -12,14 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
type Props = {
children: React.ReactNode;
};
import styles from "./BlockList.module.css";
const BlockList: React.FC<Props> = ({ children }) => {
return (
<div className="grid grid-cols-1 gap-4 group content-start">{children}</div>
);
const BlockList: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
return <div className={styles.blockList}>{children}</div>;
};
export default BlockList;

View File

@ -0,0 +1,24 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.session-icon {
color: var(--cpd-color-icon-secondary);
background: var(--cpd-color-bg-subtle-secondary);
height: var(--cpd-space-10x);
width: var(--cpd-space-10x);
padding: var(--cpd-space-2x);
border-radius: var(--cpd-space-1x);
margin-right: var(--cpd-space-2x);
}

View File

@ -22,7 +22,7 @@ import { useTransition } from "react";
import { currentBrowserSessionIdAtom, currentUserIdAtom } from "../atoms";
import { FragmentType, graphql, useFragment } from "../gql";
import Block from "./Block";
import styles from "./BrowserSession.module.css";
import DateTime from "./DateTime";
const FRAGMENT = graphql(/* GraphQL */ `
@ -92,8 +92,8 @@ const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
};
return (
<Block className="flex items-center">
<IconWebBrowser className="mr-4 session-icon" />
<div className="flex items-center">
<IconWebBrowser className={styles.sessionIcon} />
<div className="flex-1">
<Body size="md" weight="medium">
{isCurrent ? (
@ -118,7 +118,7 @@ const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
>
Sign out
</Button>
</Block>
</div>
);
};

View File

@ -82,7 +82,7 @@ const CompatSession: React.FC<{
};
return (
<Block className="p-4 bg-grey-25 dark:bg-grey-450 rounded-lg">
<Block>
<Body>
Started: <DateTime datetime={data.createdAt} />
</Body>

View File

@ -0,0 +1,31 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.container {
width: 378px;
margin: var(--cpd-space-10x) auto var(--cpd-space-6x) auto;
}
.footer {
text-align: center;
}
.separator {
border: 0;
height: 1px;
background: var(--cpd-color-border-interactive-secondary);
margin: var(--cpd-space-4x) 0;
}

View File

@ -12,24 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import styles from "./Layout.module.css";
import NavBar from "./NavBar";
import NavItem from "./NavItem";
import NavItem, { ExternalLink } from "./NavItem";
const Separator: React.FC = () => <hr className={styles.separator} />;
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
return (
<>
<NavBar className="nav-bar container">
<div className={styles.container}>
<NavBar>
<NavItem route={{ type: "home" }}>Sessions</NavItem>
<NavItem route={{ type: "account" }}>Emails</NavItem>
</NavBar>
<hr className="my-2" />
<Separator />
<main className="container">{children}</main>
<main>{children}</main>
<hr className="my-2" />
<Separator />
<footer className="text-center">
<footer className={styles.footer}>
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
<img
className="inline my-2"
@ -40,19 +43,19 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
/>
</a>
<NavBar className="nav-bar container">
<NavItem href="https://matrix.org/legal/copyright-notice">
<NavBar>
<ExternalLink href="https://matrix.org/legal/copyright-notice">
Info
</NavItem>
<NavItem href="https://matrix.org/legal/privacy-notice">
</ExternalLink>
<ExternalLink href="https://matrix.org/legal/privacy-notice">
Privacy
</NavItem>
<NavItem href="https://matrix.org/legal/terms-and-conditions">
</ExternalLink>
<ExternalLink href="https://matrix.org/legal/terms-and-conditions">
Terms & Conditions
</NavItem>
</ExternalLink>
</NavBar>
</footer>
</>
</div>
);
};

View File

@ -0,0 +1,21 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.nav-bar-items {
display: flex;
flex-direction: row;
justify-content: center;
gap: var(--cpd-space-4x);
}

View File

@ -12,12 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
const NavBar: React.FC<{
className: string;
children: React.ReactNode;
}> = ({ className, children }) => (
<nav className={className}>
<ul className="flex flex-row gap-4 justify-center">{children}</ul>
import styles from "./NavBar.module.css";
const NavBar: React.FC<React.PropsWithChildren<{}>> = ({ children }) => (
<nav>
<ul className={styles.navBarItems}>{children}</ul>
</nav>
);

View File

@ -0,0 +1,30 @@
/* Copyright 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.nav-item {
padding: var(--cpd-space-1x) var(--cpd-space-2x);
color: var(--cpd-color-text-secondary);
line-height: var(--cpd-space-6x);
border-radius: var(--cpd-radius-pill-effect);
}
.nav-item:hover {
color: var(--cpd-color-text-primary);
background-color: var(--cpd-color-bg-subtle-secondary);
}
.nav-item[aria-current="page"] {
color: var(--cpd-color-text-primary);
}

View File

@ -12,41 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Link as CpdLink } from "@vector-im/compound-web";
import { useAtomValue } from "jotai";
import { Link, Route, routeAtom } from "../Router";
type NavItemProps = {
children: React.ReactNode;
} & ({ route: Route; href?: never } | { route?: never; href: string });
import styles from "./NavItem.module.css";
function isRoute(route: Route | undefined): route is Route {
return !!route?.type;
}
function isHref(href: string | undefined): href is string {
return typeof href === "string";
}
const NavItem: React.FC<NavItemProps> = ({ route, href, children }) => {
const NavItem: React.FC<React.PropsWithChildren<{ route: Route }>> = ({
route,
children,
}) => {
const currentRoute = useAtomValue(routeAtom);
return (
<li className="m-1 mr-0">
{isRoute(route) && (
<Link
route={route}
className={currentRoute.type === route.type ? "active" : ""}
>
{children}
</Link>
)}
{isHref(href) && (
<a href={href} target="_blank" rel="noopenner noreferrer">
{children}
</a>
)}
<li>
<Link
className={styles.navItem}
route={route}
aria-current={currentRoute.type === route.type ? "page" : undefined}
>
{children}
</Link>
</li>
);
};
export const ExternalLink: React.FC<
React.PropsWithChildren<{ href: string }>
> = ({ href, children }) => (
<li>
<CpdLink href={href}>{children}</CpdLink>
</li>
);
export default NavItem;

View File

@ -99,13 +99,7 @@ const OAuth2Session: React.FC<Props> = ({ session }) => {
};
return (
<Block
className={`p-4 bg-grey-25 dark:bg-grey-450 rounded-lg ${
data.finishedAt
? "opacity-50 group-hover:opacity-100 transition-opacity"
: ""
}`}
>
<Block>
<Typography variant="body" bold>
<Link
route={{ type: "client", id: data.client.id }}

View File

@ -25,37 +25,10 @@
@tailwind utilities;
body {
/* XXX: I'm unsure why this is not part of the tokens */
--cpd-radius-pill-effect: 9999px;
font: var(--cpd-font-body-md-regular);
background: var(--cpd-color-bg-canvas-default);
width: 378px;
margin: var(--cpd-space-10x) auto var(--cpd-space-6x) auto;
}
.nav-bar a {
color: var(--cpd-color-text-action-primary);
}
.nav-bar .active {
font-weight: var(--cpd-font-weight-semibold);
}
.nav-bar li:not(:first-child) {
list-style-type: disc;
color: var(--cpd-color-text-secondary);
}
hr {
border: 0;
height: 1px;
background: var(--cpd-color-gray-400);
}
.session-icon {
color: var(--cpd-color-icon-secondary);
background: var(--cpd-color-bg-subtle-secondary);
height: var(--cpd-space-10x);
width: var(--cpd-space-10x);
padding: var(--cpd-space-2x);
border-radius: var(--cpd-space-1x);
color: var(--cpd-color-text-primary);
}

View File

@ -42,6 +42,11 @@ module.exports = {
"black-950": "#21262C",
ice: "#F4F9FD",
},
fontWeight: {
semibold: "var(--cpd-font-weight-semibold)",
medium: "var(--cpd-font-weight-medium)",
regular: "var(--cpd-font-weight-regular)",
},
},
variants: {
extend: {},

View File

@ -23,6 +23,13 @@ import { defineConfig } from "vitest/config";
export default defineConfig((env) => ({
base: "./",
css: {
modules: {
localsConvention: "camelCaseOnly",
},
},
build: {
manifest: true,
assetsDir: "",
@ -37,6 +44,7 @@ export default defineConfig((env) => ({
],
},
},
plugins: [
codegen(),
@ -104,6 +112,7 @@ export default defineConfig((env) => ({
ext: ".zz",
}),
],
server: {
base: "/account/",
proxy: {
@ -112,6 +121,7 @@ export default defineConfig((env) => ({
"http://127.0.0.1:8080",
},
},
test: {
coverage: {
provider: "v8",