diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml index e389e6371..7f82a865f 100644 --- a/.stylelintrc.yaml +++ b/.stylelintrc.yaml @@ -1,5 +1,10 @@ extends: stylelint-config-standard plugins: - stylelint-no-unsupported-browser-features + - stylelint-scss rules: + at-rule-no-unknown: null + function-no-unknown: null + import-notation: string no-descending-specificity: ~ + scss/at-rule-no-unknown: true diff --git a/bin/test-eslint b/bin/test-eslint index 084b4f876..28caf5518 100755 --- a/bin/test-eslint +++ b/bin/test-eslint @@ -6,4 +6,4 @@ cd "$(dirname "$0")/.." echo 'Running ESLint...' -./node_modules/.bin/eslint -c ./eslint.config.js ./betty ./playwright "$@" +./node_modules/.bin/eslint -c ./eslint.config.js ./betty ./playwright ./raspberry-mint-dev "$@" diff --git a/bin/test-stylelint b/bin/test-stylelint index 0ffb9cf13..a3557fb9d 100755 --- a/bin/test-stylelint +++ b/bin/test-stylelint @@ -6,4 +6,4 @@ cd "$(dirname "$0")/.." echo 'Running Stylelint...' -./node_modules/.bin/stylelint "./betty/**/*.css" +./node_modules/.bin/stylelint "./betty/**/*.css" "./raspberry-mint-dev/**/*.css" "./raspberry-mint-dev/**/*.scss" diff --git a/package.json b/package.json index 82ca19101..0b5090132 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "stylelint": "^16.10.0", "stylelint-config-standard": "^36.0.1", "stylelint-no-unsupported-browser-features": "^8.0.1", + "stylelint-scss": "^6.8.1", "typescript": "^5.6.3", "typescript-eslint": "^8.11.0" }, diff --git a/raspberry-mint-dev/.gitignore b/raspberry-mint-dev/.gitignore new file mode 100644 index 000000000..8987626e9 --- /dev/null +++ b/raspberry-mint-dev/.gitignore @@ -0,0 +1,5 @@ +/build +/cache +/node_modules +/package-lock.json + diff --git a/raspberry-mint-dev/package.json b/raspberry-mint-dev/package.json new file mode 100644 index 000000000..35dacdce1 --- /dev/null +++ b/raspberry-mint-dev/package.json @@ -0,0 +1,35 @@ +{ + "engines": { + "node": ">= 20" + }, + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", + "@babel/preset-typescript": "^7.26.0", + "@popperjs/core": "^2.11.8", + "babel-loader": "^9.2.1", + "bootstrap": "^5.3.3", + "clean-webpack-plugin": "^4.0.0", + "core-js": "^3.38.1", + "css-loader": "^7.1.2", + "css-minimizer-webpack-plugin": "^7.0.0", + "file-loader": "^6.2.0", + "mini-css-extract-plugin": "^2.9.1", + "postcss-loader": "^8.1.1", + "resolve-url-loader": "^5.0.0", + "sass": "^1.80.4", + "sass-loader": "^16.0.2", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.10", + "typescript": "^5.6.3", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0", + "html-webpack-plugin": "^5.6.3" + }, + "scripts": { + "webpack": "webpack", + "serve": "webpack serve --mode development --open" + }, + "type": "module" +} diff --git a/raspberry-mint-dev/src/css/components/button.scss b/raspberry-mint-dev/src/css/components/button.scss new file mode 100644 index 000000000..db7d618f3 --- /dev/null +++ b/raspberry-mint-dev/src/css/components/button.scss @@ -0,0 +1,7 @@ +.btn { + &:focus { + background-color: $focus; + outline: 2px $black solid; + color: $black; + } +} \ No newline at end of file diff --git a/raspberry-mint-dev/src/css/components/card.scss b/raspberry-mint-dev/src/css/components/card.scss new file mode 100644 index 000000000..e58666d00 --- /dev/null +++ b/raspberry-mint-dev/src/css/components/card.scss @@ -0,0 +1,5 @@ +.card { + background-color: $gray-100; + border:none; + box-shadow: 1px 1px $gray-300; +} diff --git a/raspberry-mint-dev/src/css/components/footer.scss b/raspberry-mint-dev/src/css/components/footer.scss new file mode 100644 index 000000000..cac671ffc --- /dev/null +++ b/raspberry-mint-dev/src/css/components/footer.scss @@ -0,0 +1,3 @@ +#footer { + background-color: $mint-900; +} diff --git a/raspberry-mint-dev/src/css/components/header.scss b/raspberry-mint-dev/src/css/components/header.scss new file mode 100644 index 000000000..7fd1281c8 --- /dev/null +++ b/raspberry-mint-dev/src/css/components/header.scss @@ -0,0 +1,15 @@ +#header { + background-color: $mint-900; +} + +.nav-brand-site-logo { + background-image: url("/betty-192x192.png"); + background-position: center left; + background-repeat: no-repeat; + background-size: 3rem; + box-shadow: none; + display: inline-block; + line-height: 3rem; + min-height: 3rem; + padding-left: 4rem; +} diff --git a/raspberry-mint-dev/src/css/dev.scss b/raspberry-mint-dev/src/css/dev.scss new file mode 100644 index 000000000..1677f02a4 --- /dev/null +++ b/raspberry-mint-dev/src/css/dev.scss @@ -0,0 +1,113 @@ +/* @todo Do not migrate these to the final product! */ +code { + color: #000 !important; + background-color: #fff; +} + +.bg-raspberry-100 { + background-color: $raspberry-100 !important; +} + +.bg-raspberry-200 { + background-color: $raspberry-200 !important; +} + +.bg-raspberry-300 { + background-color: $raspberry-300 !important; +} + +.bg-raspberry-400 { + background-color: $raspberry-400 !important; +} + +.bg-raspberry-500 { + background-color: $raspberry-500 !important; +} + +.bg-raspberry-600 { + background-color: $raspberry-600 !important; +} + +.bg-raspberry-700 { + background-color: $raspberry-700 !important; +} + +.bg-raspberry-800 { + background-color: $raspberry-800 !important; +} + +.bg-raspberry-900 { + background-color: $raspberry-900 !important; +} + +.bg-mint-100 { + background-color: $mint-100 !important; +} + +.bg-mint-200 { + background-color: $mint-200 !important; +} + +.bg-mint-300 { + background-color: $mint-300 !important; +} + +.bg-mint-400 { + background-color: $mint-400 !important; +} + +.bg-mint-500 { + background-color: $mint-500 !important; +} + +.bg-mint-600 { + background-color: $mint-600 !important; +} + +.bg-mint-700 { + background-color: $mint-700 !important; +} + +.bg-mint-800 { + background-color: $mint-800 !important; +} + +.bg-mint-900 { + background-color: $mint-900 !important; +} + +.bg-orange-100 { + background-color: $orange-100 !important; +} + +.bg-orange-200 { + background-color: $orange-200 !important; +} + +.bg-orange-300 { + background-color: $orange-300 !important; +} + +.bg-orange-400 { + background-color: $orange-400 !important; +} + +.bg-orange-500 { + background-color: $orange-500 !important; +} + +.bg-orange-600 { + background-color: $orange-600 !important; +} + +.bg-orange-700 { + background-color: $orange-700 !important; +} + +.bg-orange-800 { + background-color: $orange-800 !important; +} + +.bg-orange-900 { + background-color: $orange-900 !important; +} \ No newline at end of file diff --git a/raspberry-mint-dev/src/css/main.scss b/raspberry-mint-dev/src/css/main.scss new file mode 100644 index 000000000..05ee5aca8 --- /dev/null +++ b/raspberry-mint-dev/src/css/main.scss @@ -0,0 +1,55 @@ +/* @todo Finetune the imports and remove unneeded comments */ + +/* 1. Include functions first (so you can manipulate colors, SVGs, calc, etc) */ +@import "../../node_modules/bootstrap/scss/functions"; + +/* 2. Include any default variable overrides here */ +@import "./variables/colors"; +@import "./variables/focus"; +@import "./variables/font"; +@import "./variables/border"; + +/* 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets) */ +@import "../../node_modules/bootstrap/scss/variables"; +@import "../../node_modules/bootstrap/scss/variables-dark"; + +/* 4. Include any default map overrides here */ +$theme-colors: ( + "primary": $raspberry, + "secondary": $mint, + "danger": $orange, +); + +/* 5. Include remainder of required parts */ +@import "../../node_modules/bootstrap/scss/maps"; +@import "../../node_modules/bootstrap/scss/mixins"; +@import "../../node_modules/bootstrap/scss/root"; + +/* 6. Optionally include any other parts as needed */ +@import "../../node_modules/bootstrap/scss/utilities"; +@import "../../node_modules/bootstrap/scss/reboot"; +@import "../../node_modules/bootstrap/scss/type"; +@import "../../node_modules/bootstrap/scss/images"; +@import "../../node_modules/bootstrap/scss/containers"; +@import "../../node_modules/bootstrap/scss/grid"; +@import "../../node_modules/bootstrap/scss/helpers"; + +/* Components */ +@import "../../node_modules/bootstrap/scss/buttons"; +@import "../../node_modules/bootstrap/scss/card"; +@import "../../node_modules/bootstrap/scss/list-group"; +@import "../../node_modules/bootstrap/scss/nav"; +@import "../../node_modules/bootstrap/scss/navbar"; + +/* 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss` */ +@import "../../node_modules/bootstrap/scss/utilities/api"; + +/* 8. Add additional custom code here */ +@import "./text.scss"; +@import "./components/button.scss"; +@import "./components/card.scss"; +@import "./components/header.scss"; +@import "./components/footer.scss"; + +/* @todo Do not migrate this to the final product! */ +@import "./dev.scss"; diff --git a/raspberry-mint-dev/src/css/text.scss b/raspberry-mint-dev/src/css/text.scss new file mode 100644 index 000000000..13e01f39d --- /dev/null +++ b/raspberry-mint-dev/src/css/text.scss @@ -0,0 +1,17 @@ +a { + color: $mint-300; + box-shadow: 0 2px $mint-300; + text-decoration: none; + + &:focus { + background-color: $focus; + box-shadow: 0 0 $focus, 0 2px $black; + color: $black; + text-decoration-color: $black; + outline: none; + } + + &:hover { + color: $mint-100; + } +} diff --git a/raspberry-mint-dev/src/css/variables/border.scss b/raspberry-mint-dev/src/css/variables/border.scss new file mode 100644 index 000000000..b1eb763fe --- /dev/null +++ b/raspberry-mint-dev/src/css/variables/border.scss @@ -0,0 +1,6 @@ +$border-radius:0; +$border-radius-sm:0; +$border-radius-lg:0; +$border-radius-xl:0; +$border-radius-xxl:0; +$border-radius-pill:0; diff --git a/raspberry-mint-dev/src/css/variables/colors.scss b/raspberry-mint-dev/src/css/variables/colors.scss new file mode 100644 index 000000000..e34c8184f --- /dev/null +++ b/raspberry-mint-dev/src/css/variables/colors.scss @@ -0,0 +1,33 @@ +$mint: #3eb489; +$raspberry: #b3446c; +$orange: #ffbd22; + +$mint-100: mix(black, $mint, 80%); +$mint-200: mix(black, $mint, 60%); +$mint-300: mix(black, $mint, 40%); +$mint-400: mix(black, $mint, 20%); +$mint-500: $mint; +$mint-600: mix(white, $mint, 20%); +$mint-700: mix(white, $mint, 40%); +$mint-800: mix(white, $mint, 60%); +$mint-900: mix(white, $mint, 80%); + +$raspberry-100: mix(black, $raspberry, 80%); +$raspberry-200: mix(black, $raspberry, 60%); +$raspberry-300: mix(black, $raspberry, 40%); +$raspberry-400: mix(black, $raspberry, 20%); +$raspberry-500: $raspberry; +$raspberry-600: mix(white, $raspberry, 20%); +$raspberry-700: mix(white, $raspberry, 40%); +$raspberry-800: mix(white, $raspberry, 60%); +$raspberry-900: mix(white, $raspberry, 80%); + +$orange-100: mix(black, $orange, 80%); +$orange-200: mix(black, $orange, 60%); +$orange-300: mix(black, $orange, 40%); +$orange-400: mix(black, $orange, 20%); +$orange-500: $orange; +$orange-600: mix(white, $orange, 20%); +$orange-700: mix(white, $orange, 40%); +$orange-800: mix(white, $orange, 60%); +$orange-900: mix(white, $orange, 80%); diff --git a/raspberry-mint-dev/src/css/variables/focus.scss b/raspberry-mint-dev/src/css/variables/focus.scss new file mode 100644 index 000000000..43db1ed52 --- /dev/null +++ b/raspberry-mint-dev/src/css/variables/focus.scss @@ -0,0 +1,7 @@ +$focus: $orange-700; + +$focus-ring-width: 0; +$focus-ring-opacity: 1; +$focus-ring-color: red; +$focus-ring-blur: 0; +$focus-ring-box-shadow: 0; \ No newline at end of file diff --git a/raspberry-mint-dev/src/css/variables/font.scss b/raspberry-mint-dev/src/css/variables/font.scss new file mode 100644 index 000000000..41b83b29e --- /dev/null +++ b/raspberry-mint-dev/src/css/variables/font.scss @@ -0,0 +1,2 @@ +$font-family-base: "Noto Sans", system-ui, -apple-system, "Segoe UI", "Roboto", "Helvetica Neue", "Noto Sans", "Liberation Sans", "Arial", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +$font-size-base: 1.3rem; diff --git a/raspberry-mint-dev/src/js/main.ts b/raspberry-mint-dev/src/js/main.ts new file mode 100644 index 000000000..2ba3a0c40 --- /dev/null +++ b/raspberry-mint-dev/src/js/main.ts @@ -0,0 +1,3 @@ +import '../css/main.scss' +import * as Popper from "@popperjs/core" // eslint-disable-line @typescript-eslint/no-unused-vars +import "../../node_modules/bootstrap/js/dist/collapse" diff --git a/raspberry-mint-dev/src/www/index.html b/raspberry-mint-dev/src/www/index.html new file mode 100644 index 000000000..8697b084d --- /dev/null +++ b/raspberry-mint-dev/src/www/index.html @@ -0,0 +1,221 @@ + + + + Raspberry Mint development environment + + + + + + + +
+ Skip to main content +
+ +
+

Welcome to Betty's new theme: Raspberry Mint!

+ +
+

Colors

+ + +
+
+

Raspberry

+
    +
  • $raspberry-100
  • +
  • $raspberry-200
  • +
  • $raspberry-300
  • +
  • $raspberry-400
  • +
  • $raspberry-500
  • +
  • $raspberry-600
  • +
  • $raspberry-700
  • +
  • $raspberry-800
  • +
  • $raspberry-900
  • +
+
+ +
+

Mint

+
    +
  • $mint-100
  • +
  • $mint-200
  • +
  • $mint-300
  • +
  • $mint-400
  • +
  • $mint-500
  • +
  • $mint-600
  • +
  • $mint-700
  • +
  • $mint-800
  • +
  • $mint-900
  • +
+
+ +
+

Orange

+
    +
  • $orange-100
  • +
  • $orange-200
  • +
  • $orange-300
  • +
  • $orange-400
  • +
  • $orange-500
  • +
  • $orange-600
  • +
  • $orange-700
  • +
  • $orange-800
  • +
  • $orange-900
  • +
+
+
+
+ +
+

Text

+ +
+

Paragraph

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas risus mi, aliquam eget bibendum id, + venenatis et leo. Suspendisse potenti. Orci varius natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Morbi ultricies dapibus mauris. Ut cursus pharetra nibh, ut venenatis tellus. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam erat volutpat. Vestibulum bibendum, ex + sed convallis egestas, erat leo venenatis eros, vel elementum mauris tortor a arcu. Aenean bibendum + malesuada elit. Pellentesque eleifend, mi sit amet molestie congue, augue metus volutpat lacus, eu + posuere mauris turpis sed turpis. Proin pulvinar finibus orci sit amet consectetur. Quisque scelerisque + tempus dui ac fermentum.

+

Donec ac nunc ligula. Sed sit amet metus in diam tincidunt condimentum. Phasellus sodales odio nec arcu + tempus cursus. Aenean fermentum purus eros, molestie tincidunt magna tincidunt sed. Proin sagittis ut ex + in luctus. Suspendisse potenti. Donec sed ullamcorper metus. Morbi sed quam eget tortor commodo + congue.

+

Nam quis est est. Ut pellentesque at ligula sit amet venenatis. Phasellus id consectetur tortor. Cras + odio erat, tristique dapibus dapibus in, convallis at orci. Sed magna tortor, ultrices nec posuere quis, + gravida vel lorem. Nulla tincidunt nec neque non bibendum. Aenean finibus erat sit amet pulvinar + posuere. Cras tellus erat, imperdiet nec dolor at, egestas laoreet ante. In eget orci quis ligula porta + placerat et at quam. Morbi fringilla condimentum magna, in ornare ligula aliquam quis.

+
+ + +
+ +
+

Components

+ +
+

Cards

+ +
+

Minimal card

+
+
+

Some quick example text to build on the card title and make up the bulk of + the card's content.

+
+
+
+ +
+

Card with title and link

+
+
+
Card title
+

Some quick example text to build on the card title and make up the bulk of + the card's content.

+ Card link + Another link +
+
+
+
+ +
+

Forms

+ +
+

Button

+ +
+
+
+
+ + + diff --git a/raspberry-mint-dev/webpack.config.js b/raspberry-mint-dev/webpack.config.js new file mode 100644 index 000000000..0c02a29db --- /dev/null +++ b/raspberry-mint-dev/webpack.config.js @@ -0,0 +1,131 @@ +'use strict' + +import {CleanWebpackPlugin} from 'clean-webpack-plugin' +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin' +import HtmlWebpackPlugin from 'html-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' +import path from 'path' +import TerserPlugin from 'terser-webpack-plugin' +import url from 'node:url' + +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) + +const webpackConfiguration = { + mode: 'development', + devtool: 'eval-source-map', + entry: path.join(__dirname, 'src', 'js', 'main.ts'), + output: { + path: path.join(__dirname, 'build'), + filename: 'js/[name].js' + }, + /* @todo Do not migrate this to the final product! */ + devServer: { + watchFiles: { + paths: [ + "./src/**/*", + "../betty/assets/public/static/**/*", + ], + }, + static: { + directory: path.join(__dirname, "..", "betty", "assets", "public","static") + } + }, + optimization: { + concatenateModules: true, + minimize: false, + minimizer: [ + new CssMinimizerPlugin(), + new TerserPlugin({ + extractComments: false, + terserOptions: { + output: { + comments: false + } + } + }) + ], + }, + plugins: [ + new CleanWebpackPlugin(), + new MiniCssExtractPlugin({ + filename: 'css/[name].css' + }), + new HtmlWebpackPlugin({ template: path.join(__dirname, './src/www/index.html') }) + + ], + module: { + rules: [ + { + test: /\.(js|ts)$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + cacheDirectory: path.resolve(__dirname, 'cache'), + presets: [ + [ + '@babel/preset-env', { + debug: true, + modules: false, + useBuiltIns: 'usage', + corejs: 3 + }, + ], + '@babel/preset-typescript', + ] + } + } + ] + }, + { + test: /\.s?css$/, + use: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + publicPath: '/' + } + }, + { + loader: 'css-loader', + options: { + url: { + // Betty's own assets are generated through the assets file system, + // so we use Webpack for vendor assets only. + filter: (url, resourcePath) => resourcePath.includes('/node_modules/'), + } + } + }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: () => [ + require('autoprefixer') + ] + } + } + }, + { + loader: 'sass-loader', + options:{ + sassOptions: { + silenceDeprecations: ["color-functions", "global-builtin", "import", "mixed-decls"] + } + } + } + ] + }, + { + test: /.*\.png|gif|jpg|jpeg|svg/, + type: 'asset/resource', + generator: { + filename: 'images/[hash][ext]' + } + } + ] + } +} + +export default webpackConfiguration