From ebf0daacce7773ac1e06537ce7c71f71acf4a2b2 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Fri, 24 Oct 2025 14:19:58 +0300 Subject: [PATCH] Enhance project structure and styling for landing page. Add detailed file structure documentation in cloud.md. Update webpack configuration to support CSS Modules and SCSS. Introduce new styles for terms page and integrate them into the build process. Update package.json and package-lock.json with new dependencies for improved styling capabilities. --- cloud.md | 26 ++ ijl.config.js | 44 ++- package-lock.json | 457 +++++++++++++++++++++++++++++++ package.json | 5 + readme.md | 140 ++++++++-- src/index.tsx | 6 + src/styles/main.module.scss | 13 + src/styles/main.module.scss.d.ts | 2 + src/styles/terms.scss | 157 +++++++++++ src/terms.html | 90 +----- src/terms.js | 3 + 11 files changed, 823 insertions(+), 120 deletions(-) create mode 100644 src/styles/main.module.scss create mode 100644 src/styles/main.module.scss.d.ts create mode 100644 src/styles/terms.scss create mode 100644 src/terms.js diff --git a/cloud.md b/cloud.md index b6cd8aa..6cbd798 100644 --- a/cloud.md +++ b/cloud.md @@ -33,6 +33,32 @@ --- +## Структура файлов + +``` +bro.landing/ +├── src/ +│ ├── styles/ +│ │ ├── main.module.scss # CSS Modules для React компонентов +│ │ ├── main.module.scss.d.ts # TypeScript типы для CSS Modules +│ │ └── terms.scss # Обычный SCSS для статической страницы +│ ├── pages/ +│ │ └── under-construction/ # Главная страница (React) +│ ├── index.tsx # Entry point для главной страницы +│ ├── terms.js # Entry point для стилей Terms +│ ├── terms.html # Статический HTML Terms страницы +│ ├── index.ejs # HTML шаблон для React страницы +│ └── app.tsx # React приложение +├── dist/ # Собранные файлы +│ ├── index.html # Главная (с React) +│ ├── index.js + index.css # Бандлы главной +│ ├── terms.html # Terms (чистый HTML + CSS) +│ └── terms.css # Стили Terms +├── ijl.config.js # Webpack конфигурация +└── package.json + +``` + ## Структура проекта ``` diff --git a/ijl.config.js b/ijl.config.js index 3243c0c..fc85bd5 100644 --- a/ijl.config.js +++ b/ijl.config.js @@ -3,6 +3,7 @@ const path = require('path'); const pkg = require('./package'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const webpack = require('webpack'); const isProd = process.env.NODE_ENV === 'production'; @@ -18,7 +19,7 @@ module.exports = { webpackConfig: { entry: { index: './src/index.tsx', - // terms страница не нужен JS, только HTML + terms: './src/terms.js', // Entry для стилей terms }, output: { publicPath: isProd @@ -26,6 +27,34 @@ module.exports = { : `/static/${pkg.name}/${process.env.VERSION || pkg.version}/`, filename: '[name].js?[contenthash]', }, + module: { + rules: [ + { + test: /\.module\.scss$/, + use: [ + isProd ? MiniCssExtractPlugin.loader : 'style-loader', + { + loader: 'css-loader', + options: { + modules: { + localIdentName: isProd ? '[hash:base64:8]' : '[name]__[local]--[hash:base64:5]', + }, + }, + }, + 'sass-loader' + ], + }, + { + test: /\.scss$/, + exclude: /\.module\.scss$/, + use: [ + isProd ? MiniCssExtractPlugin.loader : 'style-loader', + 'css-loader', + 'sass-loader' + ], + }, + ], + }, plugins: [ // Главная страница (с React) new HtmlWebpackPlugin({ @@ -33,12 +62,21 @@ module.exports = { filename: 'index.html', chunks: ['index'], }), - // Terms страница (чистый HTML без JS) + // Terms страница (статика + SCSS) new HtmlWebpackPlugin({ template: './src/terms.html', filename: 'terms.html', - inject: false, // Не инжектим JS + chunks: isProd ? [] : ['terms'], // В production не нужен JS + cssPath: isProd + ? 'https://static.brojs.ru/landing/main/terms.css' + : `/static/${pkg.name}/${process.env.VERSION || pkg.version}/terms.css`, }), + // Извлечение CSS в отдельные файлы для production + ...(isProd ? [ + new MiniCssExtractPlugin({ + filename: '[name].css', + }) + ] : []), new webpack.DefinePlugin({ IS_PROD: process.env.NODE_ENV === 'production', }), diff --git a/package-lock.json b/package-lock.json index a7a05fe..cd24ccf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,13 +32,18 @@ "@eslint/js": "^9.9.0", "@types/jest": "^29.5.12", "babel-jest": "^29.7.0", + "css-loader": "^7.1.2", "eslint": "^8.57.0", "eslint-plugin-react": "^7.35.0", "eslint-plugin-react-hooks": "^4.6.2", "globals": "^15.9.0", "html-webpack-plugin": "^5.6.0", "jest": "^29.7.0", + "mini-css-extract-plugin": "^2.9.4", "puppeteer": "^24.26.1", + "sass": "^1.93.2", + "sass-loader": "^16.0.6", + "style-loader": "^4.0.0", "ts-jest": "^29.2.3", "typescript-eslint": "^8.1.0" } @@ -3144,6 +3149,316 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5607,6 +5922,20 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8024,6 +8353,13 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9839,6 +10175,27 @@ "node": ">=6" } }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", + "integrity": "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9943,6 +10300,14 @@ "tslib": "^2.0.3" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -11698,6 +12063,98 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sass": { + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "16.0.6", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.6.tgz", + "integrity": "sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", diff --git a/package.json b/package.json index 620df5e..425e692 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,18 @@ "@eslint/js": "^9.9.0", "@types/jest": "^29.5.12", "babel-jest": "^29.7.0", + "css-loader": "^7.1.2", "eslint": "^8.57.0", "eslint-plugin-react": "^7.35.0", "eslint-plugin-react-hooks": "^4.6.2", "globals": "^15.9.0", "html-webpack-plugin": "^5.6.0", "jest": "^29.7.0", + "mini-css-extract-plugin": "^2.9.4", "puppeteer": "^24.26.1", + "sass": "^1.93.2", + "sass-loader": "^16.0.6", + "style-loader": "^4.0.0", "ts-jest": "^29.2.3", "typescript-eslint": "^8.1.0" } diff --git a/readme.md b/readme.md index 3057376..32ba23a 100644 --- a/readme.md +++ b/readme.md @@ -1,45 +1,129 @@ -# BROJS.RU Landing Page +# BROJS.RU Landing -Лендинг платформы обучения фронтенд-разработке. +Лендинг платформы для обучения фронтенд-разработке -## 🚀 Быстрый старт +## 🚀 Особенности v3.0 + +- ⚡ **Минимальные зависимости** - только необходимое +- 📄 **Статический HTML** для идеального SEO +- 🎨 **SCSS + CSS Modules** для гибких стилей +- 📱 **Адаптивный дизайн** с responsive шрифтами +- 🎯 **React** только там, где нужна динамика +- 🔥 **Легкий bundle** terms.html (10 KB + 1.5 KB CSS, **без JS!**) + +## 📦 Установка ```bash -# Установка npm install +``` -# Разработка -npm start -# → http://localhost:8099/ +## 🛠️ Разработка -# Сборка -npm run build:prod +```bash +npm start # Dev сервер на http://localhost:8099 +``` + +## 🏗️ Сборка + +```bash +npm run build:prod # Production сборка в ./dist ``` ## 📄 Страницы -- `/` - главная (в разработке) -- `/terms` - пользовательское соглашение +### Главная `/` +- **React** приложение с анимацией Lottie +- Динамическая страница "В разработке" +- Файлы: `index.html` + `index.js` (916 KB) + `index.css` (190 B) -## 🔧 Команды +### Terms `/terms` +- **Чистый HTML** без JavaScript +- Полный текст пользовательского соглашения +- SEO-оптимизирован +- Адаптивный дизайн +- Файлы: `terms.html` (10.5 KB) + `terms.css` (1.5 KB) -| Команда | Описание | -|---------|----------| -| `npm start` | Dev сервер | -| `npm run build` | Dev сборка | -| `npm run build:prod` | Production + SSG | -| `npm run build:prod:ssr` | Production + SSR | +## 🎨 Стилизация -## 📦 Результат сборки - -``` -dist/ -├── index.html # Главная страница (SSG) -├── terms.html # Пользовательское соглашение (SEO) -├── index.js # React bundle -└── locales/ # i18n файлы +### CSS Modules (для React компонентов) +```typescript +import * as styles from './styles/main.module.scss'; +element.className = styles.app; ``` ---- +**Преимущества:** +- Изоляция стилей +- Автоматические уникальные имена классов +- TypeScript поддержка -📚 Полная документация: **cloud.md** +### Обычный SCSS (для статических страниц) +```scss +// terms.scss +$mobile: 768px; + +h1 { + font-size: 2.5rem; + @media (max-width: $mobile) { + font-size: 1.75rem; // Адаптивный шрифт + } +} +``` + +## 📱 Адаптивность + +Все размеры шрифтов автоматически уменьшаются на мобильных устройствах (< 768px): +- H1: 2.5rem → 1.75rem +- H2: 1.75rem → 1.5rem +- H3: 1.25rem → 1.1rem +- Body: 1rem → 0.95rem + +## 🗂️ Структура + +``` +src/ +├── styles/ +│ ├── main.module.scss # CSS Modules для React +│ ├── main.module.scss.d.ts # TypeScript типы +│ └── terms.scss # SCSS для статики +├── pages/ +│ └── under-construction/ # Главная страница +├── index.tsx # Entry для React +├── terms.js # Entry для CSS +├── terms.html # Статический HTML +└── index.ejs # Шаблон для React +``` + +## 🌐 Деплой + +После `npm run build:prod`: +- `index.html` → `brojs.ru/` +- `terms.html` → `brojs.ru/terms` +- Статика → `static.brojs.ru/landing/main/` + +## 🔧 Технологии + +### Core +- **React 18** - только для динамических частей +- **TypeScript** - типизация +- **Webpack 5** - сборка с multiple entry points +- **SCSS** - препроцессор +- **CSS Modules** - изоляция стилей + +### Библиотеки +- **React Router DOM** - клиентский роутинг +- **i18next** - интернационализация +- **Lottie React** - анимации + +### Dev зависимости +- **sass** + **sass-loader** - компиляция SCSS +- **css-loader** - обработка CSS (с поддержкой CSS Modules) +- **style-loader** - инжект CSS в dev режиме +- **mini-css-extract-plugin** - извлечение CSS в production + +## 📚 Документация + +Полная документация в [cloud.md](./cloud.md) + +## 🤝 Участие + +Проект использует минималистичный подход - добавляем зависимости только по мере необходимости! diff --git a/src/index.tsx b/src/index.tsx index ceb3d34..c658798 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,11 +4,17 @@ import { i18nextReactInitConfig } from '@brojs/cli'; import { createRoot, hydrateRoot } from 'react-dom/client' import App from './app'; +import * as styles from './styles/main.module.scss'; i18next.t = i18next.t.bind(i18next); const i18nextPromise = i18nextReactInitConfig(i18next); const MOUNT_NODE = document.getElementById('app'); +// Применяем класс к app контейнеру +if (MOUNT_NODE) { + MOUNT_NODE.className = styles.app || 'app'; +} + (async () => { await Promise.all([i18nextPromise]); diff --git a/src/styles/main.module.scss b/src/styles/main.module.scss new file mode 100644 index 0000000..542ba4a --- /dev/null +++ b/src/styles/main.module.scss @@ -0,0 +1,13 @@ +// Main styles for the landing page +body { + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; +} + +.app { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/styles/main.module.scss.d.ts b/src/styles/main.module.scss.d.ts new file mode 100644 index 0000000..a4bee34 --- /dev/null +++ b/src/styles/main.module.scss.d.ts @@ -0,0 +1,2 @@ +export const app: string; + diff --git a/src/styles/terms.scss b/src/styles/terms.scss new file mode 100644 index 0000000..048fda7 --- /dev/null +++ b/src/styles/terms.scss @@ -0,0 +1,157 @@ +// Terms page styles +$primary-color: #1a202c; +$bg-color: #f7fafc; +$white: #fff; +$text-secondary: #4a5568; +$text-muted: #718096; +$text-light: #a0aec0; +$border-color: #e2e8f0; +$link-color: #3182ce; + +$container-max-width: 1200px; +$border-radius: 8px; +$spacing-base: 1rem; + +// Breakpoints +$mobile: 768px; +$tablet: 1024px; + +// Reset +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + line-height: 1.6; + color: $primary-color; + background: $bg-color; +} + +.container { + max-width: $container-max-width; + margin: 0 auto; + padding: 40px 20px; +} + +.terms-doc { + background: $white; + padding: 60px 80px; + border-radius: $border-radius; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + + @media (max-width: 768px) { + padding: 40px 24px; + } +} + +h1 { + font-size: 2.5rem; + font-weight: 700; + color: #2d3748; + text-align: center; + margin-bottom: 0.5rem; + + @media (max-width: $mobile) { + font-size: 1.75rem; + } +} + +.subtitle { + text-align: center; + color: $text-muted; + font-size: 1.1rem; + margin-bottom: $spacing-base; + + @media (max-width: $mobile) { + font-size: 1rem; + } +} + +.date { + text-align: center; + color: $text-light; + font-size: 0.9rem; + margin-bottom: 2rem; +} + +hr { + border: none; + border-top: 1px solid $border-color; + margin: 2rem 0; +} + +h2 { + font-size: 1.75rem; + font-weight: 700; + color: #2d3748; + margin: 2rem 0 $spacing-base; + + @media (max-width: $mobile) { + font-size: 1.5rem; + margin: 1.5rem 0 0.75rem; + } +} + +h3 { + font-size: 1.25rem; + font-weight: 600; + color: $text-secondary; + margin: 1.5rem 0 0.75rem; + + @media (max-width: $mobile) { + font-size: 1.1rem; + margin: 1.25rem 0 0.5rem; + } +} + +p { + margin-bottom: $spacing-base; + color: $text-secondary; + font-size: 1rem; + + @media (max-width: $mobile) { + font-size: 0.95rem; + } +} + +ul { + margin: $spacing-base 0 $spacing-base 2rem; + color: $text-secondary; + font-size: 1rem; + + @media (max-width: $mobile) { + font-size: 0.95rem; + margin-left: 1.5rem; + } + + li { + margin-bottom: 0.5rem; + } +} + +a { + color: $link-color; + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} + +strong { + font-weight: 600; + color: #2d3748; +} + +.footer { + text-align: center; + margin-top: 3rem; + padding-top: 2rem; + border-top: 1px solid $border-color; + color: $text-light; + font-size: 0.9rem; +} + diff --git a/src/terms.html b/src/terms.html index 700b238..cc06c51 100644 --- a/src/terms.html +++ b/src/terms.html @@ -8,95 +8,7 @@ - + diff --git a/src/terms.js b/src/terms.js new file mode 100644 index 0000000..8981cc7 --- /dev/null +++ b/src/terms.js @@ -0,0 +1,3 @@ +// Entry point for terms page styles +import './styles/terms.scss'; +