refactor(styling): Remove Tailwind (#253)

* refactor(css): replace Tailwind classes with MUI sx
* chore(deps): remove classnames
* refactor(css): inline link baseline style
* feat(css): use modern-normalize
* chore(deps): remove tailwind
* refactor(css): inline all Sass files
* chore(deps): remove sass
This commit is contained in:
Jeremy Kahn 2024-04-09 20:54:41 -05:00 committed by GitHub
parent 72526ebbbb
commit 6c434f84ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 179 additions and 1098 deletions

1044
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,13 +19,13 @@
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"buffer": "^6.0.3",
"classnames": "^2.3.1",
"detectincognitojs": "^1.1.2",
"fast-memoize": "^2.5.2",
"file-saver": "^2.0.5",
"fun-animal-names": "^0.1.1",
"idb-chunk-store": "^1.0.1",
"localforage": "^1.10.0",
"modern-normalize": "^2.0.0",
"mui-markdown": "^0.5.5",
"querystring": "^0.2.1",
"react": "^18.2.0",
@ -113,13 +113,10 @@
"mprocs": "^0.6.4",
"nodemon": "^3.0.1",
"parcel": "^2.10.0",
"postcss": "^8.4.31",
"prettier": "^3.2.5",
"pretty-quick": "^4.0.0",
"process": "^0.11.10",
"sass": "^1.69.5",
"serve": "^14.1.2",
"tailwindcss": "^3.1.8",
"url": "^0.11.0",
"util": "^0.12.5",
"vite": "^5.0.13",

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -1,5 +1,4 @@
import { HTMLAttributes, useRef, useEffect, useState, useContext } from 'react'
import cx from 'classnames'
import Box from '@mui/material/Box'
import useTheme from '@mui/material/styles/useTheme'
@ -12,11 +11,7 @@ export interface ChatTranscriptProps extends HTMLAttributes<HTMLDivElement> {
userId: string
}
export const ChatTranscript = ({
className,
messageLog,
userId,
}: ChatTranscriptProps) => {
export const ChatTranscript = ({ messageLog, userId }: ChatTranscriptProps) => {
const { showRoomControls } = useContext(ShellContext)
const theme = useTheme()
const boxRef = useRef<HTMLDivElement>(null)
@ -66,11 +61,13 @@ export const ChatTranscript = ({
return (
<Box
ref={boxRef}
className={cx('ChatTranscript', className)}
className="ChatTranscript"
sx={{
display: 'flex',
flexDirection: 'column',
py: transcriptMinPadding,
flexGrow: 1,
overflow: 'auto',
pb: transcriptMinPadding,
pt: showRoomControls ? theme.spacing(10) : theme.spacing(2),
px: `max(${transcriptPaddingX}, ${transcriptMinPadding})`,
transition: `padding-top ${theme.transitions.duration.short}ms ${theme.transitions.easing.easeInOut}`,

View File

@ -0,0 +1,9 @@
import styled from '@mui/material/styles/styled'
// NOTE: These components are defined to enable raw DOM elements to be styled
// with MUI's sx prop.
// @see https://mui.com/system/styled/
// @see https://mui.com/system/getting-started/the-sx-prop/
export const Form = styled('form')({})
export const Input = styled('input')({})
export const Main = styled('main')({})

View File

@ -1,3 +0,0 @@
.Message
pre
overflow: auto

View File

@ -5,20 +5,10 @@ import Box from '@mui/material/Box'
import Tooltip from '@mui/material/Tooltip'
import Typography, { TypographyProps } from '@mui/material/Typography'
import Link, { LinkProps } from '@mui/material/Link'
import styled from '@mui/material/styles/styled'
import { materialDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
// These imports need to be ts-ignored to prevent spurious errors that look
// like this:
//
// Module 'react-markdown' cannot be imported using this construct. The
// specifier only resolves to an ES module, which cannot be imported
// synchronously. Use dynamic import instead. (tsserver 1471)
//
// @ts-ignore
import Markdown from 'react-markdown'
// @ts-ignore
import { CodeProps } from 'react-markdown/lib/ast-to-react'
// @ts-ignore
import remarkGfm from 'remark-gfm'
import {
@ -32,7 +22,7 @@ import { CopyableBlock } from 'components/CopyableBlock/CopyableBlock'
import { InlineMedia } from './InlineMedia'
import './Message.sass'
const StyledMarkdown = styled(Markdown)({})
export interface MessageProps {
message: IMessage | I_InlineMedia
@ -154,13 +144,26 @@ export const Message = ({ message, showAuthor, userId }: MessageProps) => {
) : isYouTubeLink(message) ? (
<YouTube videoId={getYouTubeVideoId(message.text)} />
) : (
<Markdown
<StyledMarkdown
components={componentMap}
remarkPlugins={[remarkGfm]}
linkTarget="_blank"
sx={{
'& pre': {
overflow: 'auto',
},
'& ol': {
pl: 2,
listStyleType: 'decimal',
},
'& ul': {
pl: 2,
listStyleType: 'disc',
},
}}
>
{message.text}
</Markdown>
</StyledMarkdown>
)}
</Box>
</Tooltip>

View File

@ -14,7 +14,7 @@ import ArrowUpward from '@mui/icons-material/ArrowUpward'
import { messageCharacterSizeLimit } from 'config/messaging'
import { SettingsContext } from 'contexts/SettingsContext'
import classNames from 'classnames'
import { Form } from 'components/Elements'
interface MessageFormProps {
onMessageSubmit: (message: string) => void
@ -76,12 +76,17 @@ export const MessageForm = ({
}
return (
<form
<Form
onSubmit={handleMessageSubmit}
className={classNames({
'pt-4 px-4': showActiveTypingStatus,
'p-4': !showActiveTypingStatus,
})}
sx={{
...(showActiveTypingStatus && {
pt: 2,
px: 2,
}),
...(!showActiveTypingStatus && {
p: 2,
}),
}}
>
<Stack direction="row" spacing={2}>
<FormControl fullWidth>
@ -110,6 +115,6 @@ export const MessageForm = ({
<ArrowUpward />
</Fab>
</Stack>
</form>
</Form>
)
}

View File

@ -157,11 +157,7 @@ export function Room({
height: landscape ? '100%' : '40%',
}}
>
<ChatTranscript
messageLog={messageLog}
userId={userId}
className="grow overflow-auto"
/>
<ChatTranscript messageLog={messageLog} userId={userId} />
<Divider />
<Box>
<MessageForm

View File

@ -8,6 +8,8 @@ import CircularProgress from '@mui/material/CircularProgress'
import { RoomContext } from 'contexts/RoomContext'
import { PeerRoom } from 'lib/PeerRoom'
import { Input } from 'components/Elements'
import { useRoomFileShare } from './useRoomFileShare'
import { MediaButton } from './MediaButton'
@ -73,12 +75,12 @@ export function RoomFileUploadControls({
px: 1,
}}
>
<input
<Input
multiple
ref={fileInputRef}
type="file"
id="file-upload"
className="hidden"
sx={{ display: 'none' }}
onChange={handleFileSelect}
/>
<Tooltip

View File

@ -1,3 +0,0 @@
.PeerDownloadFileButton
.MuiCircularProgress-circle
transition: none !important

View File

@ -10,7 +10,6 @@ import { fileTransfer } from 'lib/FileTransfer'
import { Peer } from 'models/chat'
import { ShellContext } from 'contexts/ShellContext'
import './PeerDownloadFileButton.sass'
import { usePeerNameDisplay } from 'components/PeerNameDisplay/usePeerNameDisplay'
interface PeerDownloadFileButtonProps {
@ -65,6 +64,9 @@ export const PeerDownloadFileButton = ({
<CircularProgress
variant={downloadProgress === null ? 'indeterminate' : 'determinate'}
value={downloadProgress === null ? undefined : downloadProgress}
sx={{
transition: 'none',
}}
/>
) : (
<Tooltip

20
src/index.css Normal file
View File

@ -0,0 +1,20 @@
a {
color: inherit;
text-decoration: inherit;
}
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}

View File

@ -1,9 +0,0 @@
@tailwind base
@tailwind components
@tailwind utilities
ol
@apply pl-4 list-decimal
ul
@apply pl-4 list-disc

View File

@ -2,7 +2,8 @@ import './polyfills'
import ReactDOM from 'react-dom/client'
import 'typeface-roboto'
import './index.sass'
import 'modern-normalize/modern-normalize.css'
import './index.css'
import { ThemeProvider, createTheme } from '@mui/material/styles'
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'

View File

@ -1,6 +1,7 @@
import { useContext, useEffect } from 'react'
import MuiMarkdown from 'mui-markdown'
import Box from '@mui/material/Box'
import useTheme from '@mui/material/styles/useTheme'
import { ShellContext } from 'contexts/ShellContext'
import {
@ -8,8 +9,6 @@ import {
messageCharacterSizeLimit,
} from 'config/messaging'
import './index.sass'
const messageTranscriptSizeLimitFormatted = Intl.NumberFormat().format(
messageTranscriptSizeLimit
)
@ -20,13 +19,24 @@ const messageCharacterSizeLimitFormatted = Intl.NumberFormat().format(
export const About = () => {
const { setTitle } = useContext(ShellContext)
const theme = useTheme()
useEffect(() => {
setTitle('About')
}, [setTitle])
return (
<Box className="About max-w-3xl mx-auto p-4">
<Box
className="About"
sx={{
p: 2,
mx: 'auto',
maxWidth: theme.breakpoints.values.md,
'& p': {
mb: 2,
},
}}
>
<MuiMarkdown>
{`
### User Guide

View File

@ -1,3 +0,0 @@
.About
p
@apply mb-4

View File

@ -1,20 +1,30 @@
import { useContext, useEffect } from 'react'
import Box from '@mui/material/Box'
import MuiMarkdown from 'mui-markdown'
import useTheme from '@mui/material/styles/useTheme'
import { ShellContext } from 'contexts/ShellContext'
import './index.sass'
export const Disclaimer = () => {
const { setTitle } = useContext(ShellContext)
const theme = useTheme()
useEffect(() => {
setTitle('Disclaimer')
}, [setTitle])
return (
<Box className="Disclaimer max-w-3xl mx-auto p-4">
<Box
className="Disclaimer"
sx={{
p: 2,
mx: 'auto',
maxWidth: theme.breakpoints.values.md,
'& p': {
mb: 2,
},
}}
>
<MuiMarkdown>
{`
### Interpretation and Definitions

View File

@ -1,6 +0,0 @@
.Disclaimer
ul
@apply my-4
p
@apply mb-4

View File

@ -10,22 +10,28 @@ import IconButton from '@mui/material/IconButton'
import MuiLink from '@mui/material/Link'
import GitHubIcon from '@mui/icons-material/GitHub'
import Cached from '@mui/icons-material/Cached'
import useTheme from '@mui/material/styles/useTheme'
import styled from '@mui/material/styles/styled'
import { v4 as uuid } from 'uuid'
import { routes } from 'config/routes'
import { ShellContext } from 'contexts/ShellContext'
import { PeerNameDisplay } from 'components/PeerNameDisplay'
import { Form, Main } from 'components/Elements'
import Logo from 'img/logo.svg?react'
import { EmbedCodeDialog } from './EmbedCodeDialog'
const StyledLogo = styled(Logo)({})
interface HomeProps {
userId: string
}
export function Home({ userId }: HomeProps) {
const { setTitle } = useContext(ShellContext)
const theme = useTheme()
const [roomName, setRoomName] = useState(uuid())
const [showEmbedCode, setShowEmbedCode] = useState(false)
const navigate = useNavigate()
@ -63,11 +69,29 @@ export function Home({ userId }: HomeProps) {
return (
<Box className="Home">
<main className="mt-6 px-4 max-w-3xl text-center mx-auto">
<Main
sx={{
maxWidth: theme.breakpoints.values.md,
mt: 3,
mx: 'auto',
px: 2,
textAlign: 'center',
}}
>
<Link to={routes.ABOUT}>
<Logo className="px-1 pb-4 mx-auto max-w-md" />
<StyledLogo
sx={{
px: 0.5,
pb: 2,
mx: 'auto',
maxWidth: theme.breakpoints.values.sm,
}}
/>
</Link>
<form onSubmit={handleFormSubmit} className="max-w-xl mx-auto">
<Form
onSubmit={handleFormSubmit}
sx={{ maxWidth: theme.breakpoints.values.sm, mx: 'auto' }}
>
<Typography sx={{ mb: 2 }}>
Your username:{' '}
<PeerNameDisplay paragraph={false} sx={{ fontWeight: 'bold' }}>
@ -135,10 +159,17 @@ export function Home({ userId }: HomeProps) {
Get embed code
</Button>
</Box>
</form>
</main>
</Form>
</Main>
<Divider sx={{ my: 2 }} />
<Box className="max-w-3xl text-center mx-auto px-4">
<Box
sx={{
maxWidth: theme.breakpoints.values.sm,
mx: 'auto',
textAlign: 'center',
px: 2,
}}
>
<Typography variant="body1">
This is a free communication tool that is designed for simplicity,
privacy, and security. All interaction between you and your online
@ -171,7 +202,7 @@ export function Home({ userId }: HomeProps) {
</IconButton>
</MuiLink>
</Box>
<Typography variant="body1" sx={{ textAlign: 'center' }}>
<Typography variant="body1" sx={{ textAlign: 'center', mb: 1 }}>
Licensed under{' '}
<MuiLink
href="https://github.com/jeremyckahn/chitchatter/blob/develop/LICENSE"

View File

@ -118,7 +118,7 @@ export const Settings = ({ userId }: SettingsProps) => {
const areNotificationsAvailable = notification.permission === 'granted'
return (
<Box className="max-w-3xl mx-auto p-4">
<Box sx={{ p: 2, mx: 'auto', maxWidth: theme.breakpoints.values.md }}>
<Typography
variant="h2"
sx={{

View File

@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [],
}