From 9173bff1baade985e9b8196de654690fe0faead6 Mon Sep 17 00:00:00 2001 From: Will Scullin Date: Wed, 11 Jan 2023 18:14:44 -0800 Subject: [PATCH] Switch to scss, better scope rules (#170) Restrict element types and global styling to improve embedability --- index.html | 6 + jest.config.js | 1 + js/components/App.tsx | 6 +- js/components/Apple2.tsx | 2 +- js/components/BlockDisk.tsx | 11 +- js/components/BlockFileModal.tsx | 4 +- js/components/CPUMeter.tsx | 2 +- js/components/ControlButton.tsx | 4 +- js/components/ControlStrip.tsx | 16 +-- js/components/DiskII.tsx | 11 +- js/components/DownloadModal.tsx | 6 +- js/components/Drives.tsx | 2 +- js/components/ErrorModal.tsx | 2 +- js/components/FileModal.tsx | 2 +- js/components/Header.tsx | 2 +- js/components/Inset.tsx | 2 +- js/components/Keyboard.tsx | 10 +- js/components/Modal.tsx | 19 +-- js/components/OptionsModal.tsx | 8 +- js/components/Printer.tsx | 2 +- js/components/ProgressModal.tsx | 4 +- js/components/Screen.tsx | 2 +- js/components/Tabs.tsx | 2 +- js/components/css/App.module.css | 77 ------------ js/components/css/App.module.scss | 13 ++ .../{Apple2.module.css => Apple2.module.scss} | 6 +- ...kDisk.module.css => BlockDisk.module.scss} | 0 ....module.css => BlockFileModal.module.scss} | 0 ...UMeter.module.css => CPUMeter.module.scss} | 0 js/components/css/Components.module.scss | 71 +++++++++++ js/components/css/ControlButton.module.css | 3 - js/components/css/ControlButton.module.scss | 8 ++ ...ip.module.css => ControlStrip.module.scss} | 23 ++-- .../{DiskII.module.css => DiskII.module.scss} | 0 ...l.module.css => DownloadModal.module.scss} | 0 .../{Drives.module.css => Drives.module.scss} | 0 ...odal.module.css => ErrorModal.module.scss} | 0 ...Modal.module.css => FileModal.module.scss} | 0 .../{Header.module.css => Header.module.scss} | 0 .../{Inset.module.css => Inset.module.scss} | 5 - ...yboard.module.css => Keyboard.module.scss} | 50 ++++---- .../{Modal.module.css => Modal.module.scss} | 25 ++-- ...al.module.css => OptionsModal.module.scss} | 0 ...Printer.module.css => Printer.module.scss} | 0 ...l.module.css => ProgressModal.module.scss} | 0 .../{Screen.module.css => Screen.module.scss} | 0 .../css/{Tabs.module.css => Tabs.module.scss} | 12 +- js/components/debugger/Applesoft.tsx | 4 +- js/components/debugger/CPU.tsx | 6 +- js/components/debugger/Debugger.tsx | 2 +- js/components/debugger/Disks.tsx | 4 +- js/components/debugger/FileViewer.tsx | 2 +- js/components/debugger/Memory.tsx | 8 +- js/components/debugger/VideoModes.tsx | 12 +- .../debugger/css/Applesoft.module.css | 48 ------- .../debugger/css/Applesoft.module.scss | 49 ++++++++ .../css/{CPU.module.css => CPU.module.scss} | 12 +- ...bugger.module.css => Debugger.module.scss} | 0 .../{Disks.module.css => Disks.module.scss} | 0 ...ewer.module.css => FileViewer.module.scss} | 0 .../{Memory.module.css => Memory.module.scss} | 8 +- ...odes.module.css => VideoModes.module.scss} | 0 package-lock.json | 117 +++++++++++++++++- package.json | 2 + types/styles.d.ts | 5 + webpack.config.js | 18 +++ 66 files changed, 428 insertions(+), 288 deletions(-) delete mode 100644 js/components/css/App.module.css create mode 100644 js/components/css/App.module.scss rename js/components/css/{Apple2.module.css => Apple2.module.scss} (69%) rename js/components/css/{BlockDisk.module.css => BlockDisk.module.scss} (100%) rename js/components/css/{BlockFileModal.module.css => BlockFileModal.module.scss} (100%) rename js/components/css/{CPUMeter.module.css => CPUMeter.module.scss} (100%) create mode 100644 js/components/css/Components.module.scss delete mode 100644 js/components/css/ControlButton.module.css create mode 100644 js/components/css/ControlButton.module.scss rename js/components/css/{ControlStrip.module.css => ControlStrip.module.scss} (66%) rename js/components/css/{DiskII.module.css => DiskII.module.scss} (100%) rename js/components/css/{DownloadModal.module.css => DownloadModal.module.scss} (100%) rename js/components/css/{Drives.module.css => Drives.module.scss} (100%) rename js/components/css/{ErrorModal.module.css => ErrorModal.module.scss} (100%) rename js/components/css/{FileModal.module.css => FileModal.module.scss} (100%) rename js/components/css/{Header.module.css => Header.module.scss} (100%) rename js/components/css/{Inset.module.css => Inset.module.scss} (76%) rename js/components/css/{Keyboard.module.css => Keyboard.module.scss} (90%) rename js/components/css/{Modal.module.css => Modal.module.scss} (82%) rename js/components/css/{OptionsModal.module.css => OptionsModal.module.scss} (100%) rename js/components/css/{Printer.module.css => Printer.module.scss} (100%) rename js/components/css/{ProgressModal.module.css => ProgressModal.module.scss} (100%) rename js/components/css/{Screen.module.css => Screen.module.scss} (100%) rename js/components/css/{Tabs.module.css => Tabs.module.scss} (70%) delete mode 100644 js/components/debugger/css/Applesoft.module.css create mode 100644 js/components/debugger/css/Applesoft.module.scss rename js/components/debugger/css/{CPU.module.css => CPU.module.scss} (73%) rename js/components/debugger/css/{Debugger.module.css => Debugger.module.scss} (100%) rename js/components/debugger/css/{Disks.module.css => Disks.module.scss} (100%) rename js/components/debugger/css/{FileViewer.module.css => FileViewer.module.scss} (100%) rename js/components/debugger/css/{Memory.module.css => Memory.module.scss} (97%) rename js/components/debugger/css/{VideoModes.module.css => VideoModes.module.scss} (100%) diff --git a/index.html b/index.html index 241933b..aee4958 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,12 @@ + diff --git a/jest.config.js b/jest.config.js index 700cc1b..44658ce 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,6 +3,7 @@ module.exports = { '^js/(.*)': '/js/$1', '^test/(.*)': '/test/$1', '\\.css$': 'identity-obj-proxy', + '\\.scss$': 'identity-obj-proxy', }, 'roots': [ 'js/', diff --git a/js/components/App.tsx b/js/components/App.tsx index 8e9e1fe..ba5cf81 100644 --- a/js/components/App.tsx +++ b/js/components/App.tsx @@ -1,4 +1,5 @@ import 'preact/debug'; +import cs from 'classnames'; import { h } from 'preact'; import { Header } from './Header'; import { Apple2 } from './Apple2'; @@ -7,7 +8,8 @@ import { SYSTEM_TYPE_APPLE2E } from '../ui/system'; import { SCREEN_GL } from '../ui/screen'; import { defaultSystem, systemTypes } from './util/systems'; -import styles from './css/App.module.css'; +import styles from './css/App.module.scss'; +import componentStyles from './css/Components.module.scss'; /** * Top level application component, provides the parameters @@ -26,7 +28,7 @@ export const App = () => { }; return ( -
+
{ id={`disk${number}`} className={cs(styles.diskLight, { [styles.on]: on })} /> - - + +
void; } -export const BlockFileModal = ({ smartPort, driveNo: number, onClose, isOpen } : BlockFileModalProps) => { +export const BlockFileModal = ({ smartPort, driveNo: number, onClose, isOpen }: BlockFileModalProps) => { const [handles, setHandles] = useState(); const [busy, setBusy] = useState(false); const [empty, setEmpty] = useState(true); diff --git a/js/components/CPUMeter.tsx b/js/components/CPUMeter.tsx index 0691911..131d14b 100644 --- a/js/components/CPUMeter.tsx +++ b/js/components/CPUMeter.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { Apple2 as Apple2Impl } from '../apple2'; import type { Stats } from '../apple2'; -import styles from './css/CPUMeter.module.css'; +import styles from './css/CPUMeter.module.scss'; /** * Interface for CPUMeter. diff --git a/js/components/ControlButton.tsx b/js/components/ControlButton.tsx index b3ddfd7..2f74aae 100644 --- a/js/components/ControlButton.tsx +++ b/js/components/ControlButton.tsx @@ -1,7 +1,7 @@ import { h, JSX } from 'preact'; import cs from 'classnames'; -import styles from './css/ControlButton.module.css'; +import styles from './css/ControlButton.module.scss'; /** * Interface for ControlButton. @@ -23,7 +23,7 @@ export interface ControlButtonProps { * @returns Control Button component */ export const ControlButton = ({ active, icon, title, onClick, ...props }: ControlButtonProps) => ( - ); diff --git a/js/components/ControlStrip.tsx b/js/components/ControlStrip.tsx index d0fe6b9..576f2b4 100644 --- a/js/components/ControlStrip.tsx +++ b/js/components/ControlStrip.tsx @@ -4,7 +4,7 @@ import { CPUMeter } from './CPUMeter'; import { Inset } from './Inset'; import { useHotKey } from './hooks/useHotKey'; import { AudioControl } from './AudioControl'; -import { OptionsModal} from './OptionsModal'; +import { OptionsModal } from './OptionsModal'; import { OptionsContext } from './OptionsContext'; import { Printer } from './Printer'; import { ControlButton } from './ControlButton'; @@ -13,7 +13,7 @@ import { JoyStick } from '../ui/joystick'; import { Screen, SCREEN_FULL_PAGE } from '../ui/screen'; import { System } from '../ui/system'; -import styles from './css/ControlStrip.module.css'; +import styles from './css/ControlStrip.module.scss'; import Apple2IO from 'js/apple2io'; const README = 'https://github.com/whscullin/apple2js#readme'; @@ -57,26 +57,26 @@ export const ControlStrip = ({ apple2, e, toggleDebugger }: ControlStripProps) = const doReset = useCallback(() => apple2?.reset() - , [apple2]); + , [apple2]); const doReadme = useCallback(() => window.open(README, '_blank') - , []); + , []); const doShowOptions = useCallback(() => setShowOptions(true) - , []); + , []); const doCloseOptions = useCallback(() => setShowOptions(false) - , []); + , []); const doToggleFullPage = useCallback(() => options.setOption( SCREEN_FULL_PAGE, !options.getOption(SCREEN_FULL_PAGE) ) - , [options]); + , [options]); useHotKey('F2', doToggleFullPage); useHotKey('F4', doShowOptions); @@ -90,7 +90,7 @@ export const ControlStrip = ({ apple2, e, toggleDebugger }: ControlStripProps) = -
+
diff --git a/js/components/DiskII.tsx b/js/components/DiskII.tsx index 442ea4d..5e731d0 100644 --- a/js/components/DiskII.tsx +++ b/js/components/DiskII.tsx @@ -5,10 +5,11 @@ import Disk2 from '../cards/disk2'; import { ErrorModal } from './ErrorModal'; import { FileModal } from './FileModal'; -import styles from './css/DiskII.module.css'; +import styles from './css/DiskII.module.scss'; import { DiskDragTarget } from './DiskDragTarget'; import { FLOPPY_FORMATS } from 'js/formats/types'; import { DownloadModal } from './DownloadModal'; +import { ControlButton } from './ControlButton'; /** * Storage structure for Disk II state returned via callbacks. @@ -78,12 +79,8 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => { />
- - + +
{label}
diff --git a/js/components/DownloadModal.tsx b/js/components/DownloadModal.tsx index 63af4b2..914af98 100644 --- a/js/components/DownloadModal.tsx +++ b/js/components/DownloadModal.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'preact/hooks'; import { DriveNumber, MassStorage } from '../formats/types'; import { Modal, ModalContent, ModalFooter } from './Modal'; -import styles from './css/DownloadModal.module.css'; +import styles from './css/DownloadModal.module.scss'; interface DownloadModalProps { isOpen: boolean; @@ -12,7 +12,7 @@ interface DownloadModalProps { onClose: (closeBox?: boolean) => void; } -export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen } : DownloadModalProps) => { +export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen }: DownloadModalProps) => { const [href, setHref] = useState(''); const [downloadName, setDownloadName] = useState(''); const doCancel = useCallback(() => onClose(true), [onClose]); @@ -44,7 +44,7 @@ export const DownloadModal = ({ massStorage, driveNo, onClose, isOpen } : Downlo
- { href + {href ? ( <> Disk Name: {downloadName} diff --git a/js/components/Drives.tsx b/js/components/Drives.tsx index 6a617f5..58c0980 100644 --- a/js/components/Drives.tsx +++ b/js/components/Drives.tsx @@ -13,7 +13,7 @@ import { useHash } from './hooks/useHash'; import { DISK_FORMATS, DRIVE_NUMBERS, SupportedSectors } from 'js/formats/types'; import { spawn, Ready } from './util/promises'; -import styles from './css/Drives.module.css'; +import styles from './css/Drives.module.scss'; import { DiskDragTarget } from './DiskDragTarget'; /** diff --git a/js/components/ErrorModal.tsx b/js/components/ErrorModal.tsx index 0c0b7bc..85e02e1 100644 --- a/js/components/ErrorModal.tsx +++ b/js/components/ErrorModal.tsx @@ -2,7 +2,7 @@ import { h } from 'preact'; import { useCallback } from 'preact/hooks'; import { Modal, ModalContent, ModalFooter } from './Modal'; -import styles from './css/ErrorModal.module.css'; +import styles from './css/ErrorModal.module.scss'; export interface ErrorProps { error: unknown | undefined; diff --git a/js/components/FileModal.tsx b/js/components/FileModal.tsx index bc1eb82..e79abdd 100644 --- a/js/components/FileModal.tsx +++ b/js/components/FileModal.tsx @@ -10,7 +10,7 @@ import { noAwait, spawn } from './util/promises'; import { useHash } from './hooks/useHash'; import { FileChooser, FilePickerAcceptType } from './FileChooser'; -import styles from './css/FileModal.module.css'; +import styles from './css/FileModal.module.scss'; const DISK_TYPES: FilePickerAcceptType[] = [ { diff --git a/js/components/Header.tsx b/js/components/Header.tsx index 841f68b..65f4f6a 100644 --- a/js/components/Header.tsx +++ b/js/components/Header.tsx @@ -1,6 +1,6 @@ import { h } from 'preact'; -import styles from './css/Header.module.css'; +import styles from './css/Header.module.scss'; const README = 'https://github.com/whscullin/apple2js#readme'; diff --git a/js/components/Inset.tsx b/js/components/Inset.tsx index 31d8ee4..600f651 100644 --- a/js/components/Inset.tsx +++ b/js/components/Inset.tsx @@ -1,7 +1,7 @@ import { h, ComponentChildren, JSX } from 'preact'; import cs from 'classnames'; -import styles from './css/Inset.module.css'; +import styles from './css/Inset.module.scss'; interface InsetProps extends JSX.HTMLAttributes { children: ComponentChildren; diff --git a/js/components/Keyboard.tsx b/js/components/Keyboard.tsx index 4a4b126..61785e4 100644 --- a/js/components/Keyboard.tsx +++ b/js/components/Keyboard.tsx @@ -10,7 +10,7 @@ import { mapKeyboardEvent } from './util/keyboard'; -import styles from './css/Keyboard.module.css'; +import styles from './css/Keyboard.module.scss'; /** * Convenience function for massaging key labels for upper @@ -23,7 +23,7 @@ const buildLabel = (key: string) => { const small = key.length > 1 && !key.startsWith('&'); return ( ); @@ -110,7 +110,7 @@ export interface KeyboardProps { export const Keyboard = ({ apple2, e }: KeyboardProps) => { const [pressed, setPressed] = useState([]); const [active, setActive] = useState(['LOCK']); - const keys = useMemo(() => keysAsTuples(e ? keys2e : keys2 ), [e]); + const keys = useMemo(() => keysAsTuples(e ? keys2e : keys2), [e]); // Set global keystroke handler useEffect(() => { @@ -125,7 +125,7 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => { event.preventDefault(); - const {key, keyCode, keyLabel} = mapKeyboardEvent(event, active.includes('LOCK'), active.includes('CTRL')); + const { key, keyCode, keyLabel } = mapKeyboardEvent(event, active.includes('LOCK'), active.includes('CTRL')); setPressed(pressed => pressed.concat([keyLabel])); setActive(active => active.concat([keyLabel])); @@ -152,7 +152,7 @@ export const Keyboard = ({ apple2, e }: KeyboardProps) => { if (!apple2) { return; } - const {key, keyLabel} = mapKeyboardEvent(event); + const { key, keyLabel } = mapKeyboardEvent(event); setPressed(pressed => pressed.filter(k => k !== keyLabel)); setActive(active => active.filter(k => k !== keyLabel)); diff --git a/js/components/Modal.tsx b/js/components/Modal.tsx index 3fc044a..ea51f19 100644 --- a/js/components/Modal.tsx +++ b/js/components/Modal.tsx @@ -1,9 +1,12 @@ import { h, ComponentChildren } from 'preact'; +import cs from 'classnames'; import { createPortal } from 'preact/compat'; import { useCallback } from 'preact/hooks'; import { useHotKey } from './hooks/useHotKey'; -import styles from './css/Modal.module.css'; +import styles from './css/Modal.module.scss'; +import componentStyles from './css/Components.module.scss'; +import { ControlButton } from './ControlButton'; /** * ModalOverlay creates a semi-transparent overlay in which the @@ -39,9 +42,9 @@ export const ModalContent = ({ children }: { children: ComponentChildren }) => { */ export const ModalFooter = ({ children }: { children: ComponentChildren }) => { return ( -
+
{children} -
+
); }; @@ -64,9 +67,7 @@ export const ModalCloseButton = ({ onClose }: ModalCloseButtonProp) => { useHotKey('Escape', doClose); return ( - + ); }; @@ -91,14 +92,14 @@ export interface ModalHeaderProps { */ export const ModalHeader = ({ onClose, title, icon }: ModalHeaderProps) => { return ( -
+
{icon && } {' '} {title} {onClose && } -
+
); }; @@ -132,7 +133,7 @@ export const Modal = ({ return ( isOpen ? createPortal(( -
+
{title && } {children}
diff --git a/js/components/OptionsModal.tsx b/js/components/OptionsModal.tsx index 7a02783..99113a9 100644 --- a/js/components/OptionsModal.tsx +++ b/js/components/OptionsModal.tsx @@ -11,7 +11,7 @@ import { SelectOption, } from '../options'; -import styles from './css/OptionsModal.module.css'; +import styles from './css/OptionsModal.module.scss'; /** * Boolean property interface @@ -29,7 +29,7 @@ interface BooleanProps { * @param setValue Value setter * @returns Boolean component */ -const Boolean = ({ option, value, setValue } : BooleanProps) => { +const Boolean = ({ option, value, setValue }: BooleanProps) => { const { label, name } = option; const onChange = useCallback( (event: JSX.TargetedMouseEvent) => @@ -67,7 +67,7 @@ interface SelectProps { * @param setValue Value setter * @returns Select component */ -const Select = ({ option, value, setValue } : SelectProps) => { +const Select = ({ option, value, setValue }: SelectProps) => { const { label, name } = option; const onChange = useCallback( (event: JSX.TargetedMouseEvent) => { @@ -110,7 +110,7 @@ export interface OptionsModalProps { export const OptionsModal = ({ isOpen, onClose }: OptionsModalProps) => { const options = useContext(OptionsContext); const sections = options.getSections(); - const setValue = useCallback(( name: string, value: string | boolean ) => { + const setValue = useCallback((name: string, value: string | boolean) => { options.setOption(name, value); }, [options]); diff --git a/js/components/Printer.tsx b/js/components/Printer.tsx index 1e76b2b..8d4aae6 100644 --- a/js/components/Printer.tsx +++ b/js/components/Printer.tsx @@ -4,7 +4,7 @@ import Apple2IO, { slot } from 'js/apple2io'; import Parallel, { ParallelOptions } from 'js/cards/parallel'; import { Modal, ModalContent, ModalFooter } from './Modal'; -import styles from './css/Printer.module.css'; +import styles from './css/Printer.module.scss'; import { ControlButton } from './ControlButton'; import { byte } from 'js/types'; diff --git a/js/components/ProgressModal.tsx b/js/components/ProgressModal.tsx index d16629a..98e4b61 100644 --- a/js/components/ProgressModal.tsx +++ b/js/components/ProgressModal.tsx @@ -1,7 +1,7 @@ import { h } from 'preact'; import { Modal, ModalContent } from './Modal'; -import styles from './css/ProgressModal.module.css'; +import styles from './css/ProgressModal.module.scss'; export interface ErrorProps { title: string; @@ -9,7 +9,7 @@ export interface ErrorProps { total: number | undefined; } -export const ProgressModal = ({ title, current, total } : ErrorProps) => { +export const ProgressModal = ({ title, current, total }: ErrorProps) => { if (current && total) { return ( diff --git a/js/components/Screen.tsx b/js/components/Screen.tsx index 0ad61a9..10e58d2 100644 --- a/js/components/Screen.tsx +++ b/js/components/Screen.tsx @@ -1,6 +1,6 @@ import { h, Ref } from 'preact'; -import styles from './css/Screen.module.css'; +import styles from './css/Screen.module.scss'; /** * Screen properties diff --git a/js/components/Tabs.tsx b/js/components/Tabs.tsx index d66a109..8434021 100644 --- a/js/components/Tabs.tsx +++ b/js/components/Tabs.tsx @@ -2,7 +2,7 @@ import { ComponentChild, ComponentChildren, h } from 'preact'; import { useCallback, useState } from 'preact/hooks'; import cs from 'classnames'; -import styles from './css/Tabs.module.css'; +import styles from './css/Tabs.module.scss'; export interface TabProps { children: ComponentChildren; diff --git a/js/components/css/App.module.css b/js/components/css/App.module.css deleted file mode 100644 index ac7c77d..0000000 --- a/js/components/css/App.module.css +++ /dev/null @@ -1,77 +0,0 @@ -body { - margin: 16px 0; - font-size: 14px; - background-color: #c4c1a0; /* Pantone 453 */ - font-family: sans-serif; -} - -:global(.full-page) { - background: black; -} - -a[role="button"] { - text-decoration: none; -} - -button, -a[role="button"], -input[type="file"]::file-selector-button { - background: #44372c; - color: #fff; - padding: 2px 8px; - border: 1px outset #66594e; - border-radius: 3px; - font-size: 15px; - min-width: 75px; -} - -button:hover, -a[role="button"]:hover, -input[type="file"]::file-selector-button { - background-color: #55473d; - border: 1px outset #66594e; -} - -button:active, -a[role="button"]:active, -input[type="file"]::file-selector-button { - background-color: #22150a; - border: 1px outset #44372c; -} - -button:focus, -a[role="button"]:focus, -input[type="file"]::file-selector-button { - outline: none; -} - -.container { - display: flex; - flex-direction: column; -} - -input[type="checkbox"] { - appearance: none; - background-color: #65594d; - border: 1px inset #65594d; - padding: 7px; - top: 7px; - border-radius: 3px; - display: inline-block; - position: relative; -} - -input[type="checkbox"]:checked { - background-color: #65594d; - border: 1px inset #65594d; - color: #0d0; -} - -input[type="checkbox"]:checked::after { - content: "\2716"; - font-size: 12px; - position: absolute; - top: 0; - left: 2px; - color: #0d0; -} diff --git a/js/components/css/App.module.scss b/js/components/css/App.module.scss new file mode 100644 index 0000000..cf5f9e5 --- /dev/null +++ b/js/components/css/App.module.scss @@ -0,0 +1,13 @@ +:global(.full-page) { + background: black; +} + +.container { + font-size: 14px; + font-family: sans-serif; + line-height: normal; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} diff --git a/js/components/css/Apple2.module.css b/js/components/css/Apple2.module.scss similarity index 69% rename from js/components/css/Apple2.module.css rename to js/components/css/Apple2.module.scss index 05d4ca2..cc098ac 100644 --- a/js/components/css/Apple2.module.css +++ b/js/components/css/Apple2.module.scss @@ -6,8 +6,8 @@ .outer { width: 620px; display: none; -} -.outer.ready { - display: block; + &.ready { + display: block; + } } diff --git a/js/components/css/BlockDisk.module.css b/js/components/css/BlockDisk.module.scss similarity index 100% rename from js/components/css/BlockDisk.module.css rename to js/components/css/BlockDisk.module.scss diff --git a/js/components/css/BlockFileModal.module.css b/js/components/css/BlockFileModal.module.scss similarity index 100% rename from js/components/css/BlockFileModal.module.css rename to js/components/css/BlockFileModal.module.scss diff --git a/js/components/css/CPUMeter.module.css b/js/components/css/CPUMeter.module.scss similarity index 100% rename from js/components/css/CPUMeter.module.css rename to js/components/css/CPUMeter.module.scss diff --git a/js/components/css/Components.module.scss b/js/components/css/Components.module.scss new file mode 100644 index 0000000..ccb4ed2 --- /dev/null +++ b/js/components/css/Components.module.scss @@ -0,0 +1,71 @@ +.components { + font-size: 14px; + font-family: sans-serif; + + a[role="button"] { + text-decoration: none; + } + + button, + a[role="button"], + input[type="file"]::file-selector-button { + background: #44372c; + color: #fff; + padding: 2px 8px; + border: 1px outset #66594e; + border-radius: 3px; + font-size: 15px; + min-width: 75px; + + &.iconButton { + min-width: 36px; + margin: 0 2px; + } + } + + button:hover, + a[role="button"]:hover, + input[type="file"]::file-selector-button { + background-color: #55473d; + border: 1px outset #66594e; + } + + button:active, + a[role="button"]:active, + input[type="file"]::file-selector-button { + background-color: #22150a; + border: 1px outset #44372c; + } + + button:focus, + a[role="button"]:focus, + input[type="file"]::file-selector-button { + outline: none; + } + + input[type="checkbox"] { + appearance: none; + background-color: #65594d; + border: 1px inset #65594d; + padding: 7px; + top: 7px; + border-radius: 3px; + display: inline-block; + position: relative; + } + + input[type="checkbox"]:checked { + background-color: #65594d; + border: 1px inset #65594d; + color: #0d0; + } + + input[type="checkbox"]:checked::after { + content: "\2716"; + font-size: 12px; + position: absolute; + top: 0; + left: 2px; + color: #0d0; + } +} diff --git a/js/components/css/ControlButton.module.css b/js/components/css/ControlButton.module.css deleted file mode 100644 index b333f9b..0000000 --- a/js/components/css/ControlButton.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.active { - color: #0f0; -} diff --git a/js/components/css/ControlButton.module.scss b/js/components/css/ControlButton.module.scss new file mode 100644 index 0000000..5da8f35 --- /dev/null +++ b/js/components/css/ControlButton.module.scss @@ -0,0 +1,8 @@ +.active { + color: #0f0; +} + +.iconButton { + min-width: 36px !important; + margin: 0 2px; +} diff --git a/js/components/css/ControlStrip.module.css b/js/components/css/ControlStrip.module.scss similarity index 66% rename from js/components/css/ControlStrip.module.css rename to js/components/css/ControlStrip.module.scss index e28a5c1..f999470 100644 --- a/js/components/css/ControlStrip.module.css +++ b/js/components/css/ControlStrip.module.scss @@ -17,19 +17,20 @@ justify-content: center; align-items: center; user-select: none; -} -.reset:hover { - background: #44372c; - border: 3px outset #66594e; -} -.reset:active { - background-color: #22150a; - border-left: 3px solid #44372c; - border-top: 3px solid #44372c; - border-right: 3px solid #000; - border-bottom: 3px solid #000; + &:hover { + background: #44372c; + border: 3px outset #66594e; + } + + &:active { + background-color: #22150a; + border-left: 3px solid #44372c; + border-top: 3px solid #44372c; + border-right: 3px solid #000; + border-bottom: 3px solid #000; + } } :global(.full-page) .reset { diff --git a/js/components/css/DiskII.module.css b/js/components/css/DiskII.module.scss similarity index 100% rename from js/components/css/DiskII.module.css rename to js/components/css/DiskII.module.scss diff --git a/js/components/css/DownloadModal.module.css b/js/components/css/DownloadModal.module.scss similarity index 100% rename from js/components/css/DownloadModal.module.css rename to js/components/css/DownloadModal.module.scss diff --git a/js/components/css/Drives.module.css b/js/components/css/Drives.module.scss similarity index 100% rename from js/components/css/Drives.module.css rename to js/components/css/Drives.module.scss diff --git a/js/components/css/ErrorModal.module.css b/js/components/css/ErrorModal.module.scss similarity index 100% rename from js/components/css/ErrorModal.module.css rename to js/components/css/ErrorModal.module.scss diff --git a/js/components/css/FileModal.module.css b/js/components/css/FileModal.module.scss similarity index 100% rename from js/components/css/FileModal.module.css rename to js/components/css/FileModal.module.scss diff --git a/js/components/css/Header.module.css b/js/components/css/Header.module.scss similarity index 100% rename from js/components/css/Header.module.css rename to js/components/css/Header.module.scss diff --git a/js/components/css/Inset.module.css b/js/components/css/Inset.module.scss similarity index 76% rename from js/components/css/Inset.module.css rename to js/components/css/Inset.module.scss index 1adea2f..a92dab9 100644 --- a/js/components/css/Inset.module.css +++ b/js/components/css/Inset.module.scss @@ -10,8 +10,3 @@ :global(.full-page) .inset { display: none; } - -.inset button { - min-width: 36px; - margin: 0 2px; -} diff --git a/js/components/css/Keyboard.module.css b/js/components/css/Keyboard.module.scss similarity index 90% rename from js/components/css/Keyboard.module.css rename to js/components/css/Keyboard.module.scss index 6639799..ba5d719 100644 --- a/js/components/css/Keyboard.module.css +++ b/js/components/css/Keyboard.module.scss @@ -70,6 +70,26 @@ /* border: 5px outset #66594E; */ border-radius: 5px; cursor: pointer; + + .active { + color: #4f4; + } + + .small { + font-size: 7px; + } + + div { + border-radius: 3px; + position: absolute; + bottom: 0; + width: 100%; + text-align: center; + } + + span { + font-size: 13px; + } } .pressed { @@ -82,14 +102,6 @@ /* border: 5px outset #44372C; */ } -.key div { - border-radius: 3px; - position: absolute; - bottom: 0; - width: 100%; - text-align: center; -} - .vCenter div { bottom: 10px; } @@ -99,22 +111,6 @@ line-height: 10px; } -.key span { - font-size: 13px; -} - -.key .small { - font-size: 7px; -} - -.key-SHIFT { - width: 53px; -} - -.active { - color: #4f4; -} - .key-RETURN { width: 52px; } @@ -134,10 +130,10 @@ background-color: #ffd; color: black; border-radius: 2px; -} -.key-POWER div { - bottom: 15px; + div { + bottom: 15px; + } } .key-nbsp { diff --git a/js/components/css/Modal.module.css b/js/components/css/Modal.module.scss similarity index 82% rename from js/components/css/Modal.module.css rename to js/components/css/Modal.module.scss index a8c4dca..5a1cb6b 100644 --- a/js/components/css/Modal.module.css +++ b/js/components/css/Modal.module.scss @@ -9,11 +9,11 @@ justify-content: center; background: rgb(0 0 0 / 60%); z-index: 1; -} -.modalOverlay button { - min-width: 36px; - margin: 0 2px; + button { + min-width: 36px; + margin: 0 2px; + } } .modal { @@ -36,10 +36,10 @@ border: 1px outset #66594e; border-radius: 3px; user-select: none; -} -.modalHeader button { - min-width: 36px; + button { + min-width: 36px; + } } .modalTitle { @@ -62,10 +62,11 @@ .modalFooter { text-align: right; user-select: none; -} -.modalFooter a[role="button"], -.modalFooter button { - margin: 0 0 0 5px; - min-width: 75px; + + a[role="button"], + button { + margin: 0 0 0 5px; + min-width: 75px; + } } diff --git a/js/components/css/OptionsModal.module.css b/js/components/css/OptionsModal.module.scss similarity index 100% rename from js/components/css/OptionsModal.module.css rename to js/components/css/OptionsModal.module.scss diff --git a/js/components/css/Printer.module.css b/js/components/css/Printer.module.scss similarity index 100% rename from js/components/css/Printer.module.css rename to js/components/css/Printer.module.scss diff --git a/js/components/css/ProgressModal.module.css b/js/components/css/ProgressModal.module.scss similarity index 100% rename from js/components/css/ProgressModal.module.css rename to js/components/css/ProgressModal.module.scss diff --git a/js/components/css/Screen.module.css b/js/components/css/Screen.module.scss similarity index 100% rename from js/components/css/Screen.module.css rename to js/components/css/Screen.module.scss diff --git a/js/components/css/Tabs.module.css b/js/components/css/Tabs.module.scss similarity index 70% rename from js/components/css/Tabs.module.css rename to js/components/css/Tabs.module.scss index 3a3e703..1e0c8bd 100644 --- a/js/components/css/Tabs.module.css +++ b/js/components/css/Tabs.module.scss @@ -6,13 +6,13 @@ font-weight: bold; padding: 4px; border-radius: 4px 4px 0 0; -} -.tab.selected { - background-color: #c4c1a0; - border-bottom: none; - margin-bottom: -2px; - color: #080; + .selected { + background-color: #c4c1a0; + border-bottom: none; + margin-bottom: -2px; + color: #080; + } } .tabs { diff --git a/js/components/debugger/Applesoft.tsx b/js/components/debugger/Applesoft.tsx index 1443722..5c6b0a7 100644 --- a/js/components/debugger/Applesoft.tsx +++ b/js/components/debugger/Applesoft.tsx @@ -6,8 +6,8 @@ import ApplesoftDecompiler from 'js/applesoft/decompiler'; import { ApplesoftHeap, ApplesoftVariable } from 'js/applesoft/heap'; import { Apple2 as Apple2Impl } from 'js/apple2'; -import styles from './css/Applesoft.module.css'; -import debuggerStyles from './css/Debugger.module.css'; +import styles from './css/Applesoft.module.scss'; +import debuggerStyles from './css/Debugger.module.scss'; export interface ApplesoftProps { apple2: Apple2Impl | undefined; diff --git a/js/components/debugger/CPU.tsx b/js/components/debugger/CPU.tsx index 7ac4d87..2a43245 100644 --- a/js/components/debugger/CPU.tsx +++ b/js/components/debugger/CPU.tsx @@ -8,8 +8,8 @@ import { loadLocalBinaryFile } from '../util/files'; import { spawn } from '../util/promises'; import { toHex } from 'js/util'; -import styles from './css/CPU.module.css'; -import debuggerStyles from './css/Debugger.module.css'; +import styles from './css/CPU.module.scss'; +import debuggerStyles from './css/Debugger.module.scss'; export interface CPUProps { apple2: Apple2Impl | undefined; @@ -29,7 +29,7 @@ const VALID_PAGE = /^[0-9A-F]{1,2}$/i; const VALID_ADDRESS = /^[0-9A-F]{1,4}$/i; const ERROR_ICON = ( -
+
{ +const LanguageCardMap = ({ lc, children }: LanguageCardMapProps) => { return (
diff --git a/js/components/debugger/VideoModes.tsx b/js/components/debugger/VideoModes.tsx index ec0afaa..9697892 100644 --- a/js/components/debugger/VideoModes.tsx +++ b/js/components/debugger/VideoModes.tsx @@ -5,8 +5,8 @@ import cs from 'classnames'; import { Apple2 as Apple2Impl } from 'js/apple2'; import { VideoPage } from 'js/videomodes'; -import styles from './css/VideoModes.module.css'; -import debuggerStyles from './css/Debugger.module.css'; +import styles from './css/VideoModes.module.scss'; +import debuggerStyles from './css/Debugger.module.scss'; export interface VideoModesProps { apple2: Apple2Impl | undefined; @@ -62,13 +62,13 @@ export const VideoModes = ({ apple2 }: VideoModesProps) => { return (
-
+
Text/Lores Page 1
-
+
Text/Lores Page 2
@@ -76,13 +76,13 @@ export const VideoModes = ({ apple2 }: VideoModesProps) => {
-
+
Hires Page 1
-
+
Hires Page 2
diff --git a/js/components/debugger/css/Applesoft.module.css b/js/components/debugger/css/Applesoft.module.css deleted file mode 100644 index 78c44b3..0000000 --- a/js/components/debugger/css/Applesoft.module.css +++ /dev/null @@ -1,48 +0,0 @@ -.listing { - width: calc(100% - 12px); - height: 320px; - overflow: auto; - white-space: pre-wrap; -} - -.variables { - width: 100%; - height: 320px; - overflow: auto; -} - -.variables table { - width: 100%; -} - -.variables td { - background-color: #fff; - border: 1px inset; - white-space: pre; - font-family: monospace; -} - -.internals { - width: 100%; -} - -.internals table { - width: 100%; -} - -.internals td { - background-color: #fff; - border: 1px inset; - white-space: pre; - font-family: monospace; - width: 30%; -} - -.internals th { - width: 20%; - text-align: right; -} - -.stack { - width: 10em; -} diff --git a/js/components/debugger/css/Applesoft.module.scss b/js/components/debugger/css/Applesoft.module.scss new file mode 100644 index 0000000..20e3978 --- /dev/null +++ b/js/components/debugger/css/Applesoft.module.scss @@ -0,0 +1,49 @@ +.listing { + width: calc(100% - 12px); + height: 320px; + overflow: auto; + white-space: pre-wrap; +} + +.variables { + width: 100%; + height: 320px; + overflow: auto; + + table { + width: 100%; + } + + td { + background-color: #fff; + border: 1px inset; + white-space: pre; + font-family: monospace; + } +} + +.internals { + width: 100%; + + table { + width: 100%; + } + + + td { + background-color: #fff; + border: 1px inset; + white-space: pre; + font-family: monospace; + width: 30%; + } + + th { + width: 20%; + text-align: right; + } +} + +.stack { + width: 10em; +} diff --git a/js/components/debugger/css/CPU.module.css b/js/components/debugger/css/CPU.module.scss similarity index 73% rename from js/components/debugger/css/CPU.module.css rename to js/components/debugger/css/CPU.module.scss index b953324..5b21f95 100644 --- a/js/components/debugger/css/CPU.module.css +++ b/js/components/debugger/css/CPU.module.scss @@ -23,13 +23,13 @@ color: #f00; } -div.invalid { +.errorIcon { position: relative; display: inline-block; -} -div.invalid i { - position: absolute; - top: -9px; - left: -16px; + i { + position: absolute; + top: -9px; + left: -16px; + } } diff --git a/js/components/debugger/css/Debugger.module.css b/js/components/debugger/css/Debugger.module.scss similarity index 100% rename from js/components/debugger/css/Debugger.module.css rename to js/components/debugger/css/Debugger.module.scss diff --git a/js/components/debugger/css/Disks.module.css b/js/components/debugger/css/Disks.module.scss similarity index 100% rename from js/components/debugger/css/Disks.module.css rename to js/components/debugger/css/Disks.module.scss diff --git a/js/components/debugger/css/FileViewer.module.css b/js/components/debugger/css/FileViewer.module.scss similarity index 100% rename from js/components/debugger/css/FileViewer.module.css rename to js/components/debugger/css/FileViewer.module.scss diff --git a/js/components/debugger/css/Memory.module.css b/js/components/debugger/css/Memory.module.scss similarity index 97% rename from js/components/debugger/css/Memory.module.css rename to js/components/debugger/css/Memory.module.scss index 7995ee6..ab0f42b 100644 --- a/js/components/debugger/css/Memory.module.css +++ b/js/components/debugger/css/Memory.module.scss @@ -130,19 +130,19 @@ position: relative; } -div.read { +.read { background-color: #8f8; } -div.write { +.write { background-color: #f88; } -div.read.write { +.read.write { background-color: #88f; } -div.inactive { +.inactive { background-color: #bbb; } diff --git a/js/components/debugger/css/VideoModes.module.css b/js/components/debugger/css/VideoModes.module.scss similarity index 100% rename from js/components/debugger/css/VideoModes.module.css rename to js/components/debugger/css/VideoModes.module.scss diff --git a/package-lock.json b/package-lock.json index baabd72..0a5a278 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,8 @@ "node-forge": "^1.3.0", "raw-loader": "^4.0.0", "rimraf": "^3.0.2", + "sass": "^1.57.1", + "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "stylelint": "^14.9.1", "stylelint-config-css-modules": "^4.1.0", @@ -4960,9 +4962,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001342", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz", - "integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==", + "version": "1.0.30001442", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", + "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", "dev": true, "funding": [ { @@ -7730,6 +7732,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz", + "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -11631,6 +11639,15 @@ "node": ">=6" } }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/known-css-properties": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", @@ -13482,6 +13499,61 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sass": { + "version": "1.57.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz", + "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, "node_modules/saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -19663,9 +19735,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001342", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz", - "integrity": "sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA==", + "version": "1.0.30001442", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz", + "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==", "dev": true }, "canvas": { @@ -21753,6 +21825,12 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, + "immutable": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz", + "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -24709,6 +24787,12 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, "known-css-properties": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", @@ -26079,6 +26163,27 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass": { + "version": "1.57.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz", + "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", diff --git a/package.json b/package.json index 2185c1a..ef3fc8e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,8 @@ "node-forge": "^1.3.0", "raw-loader": "^4.0.0", "rimraf": "^3.0.2", + "sass": "^1.57.1", + "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "stylelint": "^14.9.1", "stylelint-config-css-modules": "^4.1.0", diff --git a/types/styles.d.ts b/types/styles.d.ts index 8b00847..ee7d006 100644 --- a/types/styles.d.ts +++ b/types/styles.d.ts @@ -2,3 +2,8 @@ declare module '*.module.css' { const classes: { [key: string]: string }; export default classes; } + +declare module '*.module.scss' { + const classes: { [key: string]: string }; + export default classes; +} diff --git a/webpack.config.js b/webpack.config.js index 7cf6efa..35c4c2f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -38,6 +38,24 @@ const baseConfig = { 'css-loader' ], exclude: /\.module\.css$/ + }, + { + test: /\.module\.s(a|c)ss$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + import: false, + modules: { + localIdentName: '[path][name]__[local]', + } + } + }, + { + loader: 'sass-loader', + } + ], } ], },