diff --git a/package-lock.json b/package-lock.json index 08713f1..597651d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "eslint": "^9.0.0", "eslint-plugin-svelte": "^2.36.0", "globals": "^15.0.0", + "mdsvex": "^0.12.3", "postcss": "^8.4.47", "postcss-nested": "^6.2.0", "svelte": "^4.2.7", @@ -1583,6 +1584,12 @@ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.7.0.tgz", @@ -3196,6 +3203,21 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, + "node_modules/mdsvex": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.12.3.tgz", + "integrity": "sha512-C/uIJamjNo5PHHnR3JHqsBPoLcfUBpzRmAEB6FLMXI/s7XHOceswjDMKqSPEW2WHmYpKm0taZ3U20GSyhMridA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.3", + "prism-svelte": "^0.4.7", + "prismjs": "^1.17.1", + "vfile-message": "^2.0.4" + }, + "peerDependencies": { + "svelte": "^3.56.0 || ^4.0.0 || ^5.0.0-next.120" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3720,6 +3742,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prism-svelte": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/prism-svelte/-/prism-svelte-0.4.7.tgz", + "integrity": "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==", + "dev": true + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4547,6 +4584,19 @@ "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unplugin": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz", @@ -4655,6 +4705,20 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.7", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", diff --git a/package.json b/package.json index ca1fe62..c0209df 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "eslint": "^9.0.0", "eslint-plugin-svelte": "^2.36.0", "globals": "^15.0.0", + "mdsvex": "^0.12.3", "postcss": "^8.4.47", "postcss-nested": "^6.2.0", "svelte": "^4.2.7", diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 3fd16fc..bbd8e45 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,6 @@ @@ -9,6 +10,8 @@ +
+ diff --git a/src/routes/blog/html-in-css/+page.svx b/src/routes/blog/html-in-css/+page.svx new file mode 100644 index 0000000..c41b72e --- /dev/null +++ b/src/routes/blog/html-in-css/+page.svx @@ -0,0 +1,279 @@ +--- +title: "DRY-фреймворк: как сократить HTML-код и писать только на CSS" +--- + +
+ 2024-08-01 +
+ +# {title} + +## О проекте + +### Принцип DRY + +Представьте: у вас есть страница со скопированным кодом. Если заменить одну строку в CSS, придется вносить изменения в зависимые блоки. А если вдруг забыли их поменять, то, скорее всего, вас будет ждать долгий поиск и мучительное решение проблемы. + +Чтобы предотвратить ошибку, я решил придерживаться главной заповеди чистого кода — DRY, Don’t Repeat Yourself. Принцип помогает сократить повторяющийся код за счет разделения проекта на мелкие независимые модули. Туда можно с легкостью вносить новые изменения — другие блоки не пострадают. + +DRY замотивировал меня разработать такой проект, в котором принцип осуществлялся на 110%. Изначально, чтобы сверстать страницу, нужно было описать структуру документа в HTML, а потом — в селекторах на CSS. Приходилось проделывать работу дважды. + +Стало интересно, что будет, если поменять структуру документа. Перестать тратить время на написание кода в двух местах и сконцентрироваться только на CSS? В своем фреймворке я оптимизировал структуру и сделал ее намного удобнее (вот только для кого?). + +### Работа с фреймворком + +Кратко расскажу о принципе работы фреймворка. HTML in CSS — это способ избежать повторяющийся код. В нем можно писать только на CSS, HTML будет генерироваться самостоятельно. + +Перед вами стандартная страница с версткой: + +```html + + + + +

Заголовок

+ +
+ Крутой контент +
+ +``` + +Синьоры и сеньориты среди вас, вероятно, заметили, что лишняя информация находится: +- в HTML — `h1 id="title"`; +- в CSS — `h1#title {`; +- в HTML — `main id="main"`; +- в CSS — `main#main`. + +HTML полностью повторяет код в CSS, поэтому я решил убрать содержимое тега ``. Без него мы не потеряем ничего, кроме контента: + +```html + + + + +``` + +### Особенности фреймворка + +HTML in CSS можно использовать практически на любом коде сайта, но есть несколько оговорок. + +Селекторы могут создать только один элемент и не более — двух одинаковых id не бывает. + +```css +#parent > #child {} +``` + +Текстовое содержание должно находиться в псевдоэлементах, иначе не получится передать внутреннее содержимое тега через CSS. + +```css +#kopyl-link::before { + content: "Ссылка на наш сайт"; +} +``` + +Страница может запускаться только в рантайме, поэтому о SEO-оптимизации стоит забыть. Это норма для многих современных фреймворков, если не говорить об их SSR-вариациях. + +## Работа с элементами + +О проекте и его особенностях мы узнали. Теперь рассмотрим, как использовать ссылки, вложенность, порядок и другие функции с помощью инструмента. + +
+У фреймворка есть «козырь в рукаве» — это селекторы атрибутов. Он предлагает широкий выбор элементов для разработки: от картинок и видео до айфреймов. Мы часто будем использовать его в наших примерах. +
+ +### Ссылки + +
+
+ + **CSS код** + +```css +a#kopyl-link[href="kopyl.dev"] { + display: block; +} + +#kopyl-link::before { + content: "Ссылка на наш сайт"; +} +``` + +
+
+ + **Сгенерированный HTML** + +```html + +``` + +
+
+ + +А если хотите, чтобы ссылка открывалась в новой вкладке? + +
+
+ + **CSS код** + +```css +a#kopyl-link[href="kopyl.dev"][target="_blank"] { + display: block; +} +``` + +
+
+ + **Сгенерированный HTML** + +```html + +``` + +
+
+ +### Вложенность + +
+
+ + **CSS код** + +```css +#parent { + width: 200px; + height: 200px; + background-color: blue; +} + +#parent > #child { + width: 100px; + height: 100px; + background-color: green; +} +``` + +
+
+ + **Сгенерированный HTML** + +```html +
+
+
+``` + +
+
+ +### Порядок + +Используя селекторы псевдоклассов `:nth-child`, `:first-child`, `:last-child`, можно задать порядок элементов. + +
+
+ + **CSS код** + +```css +#parent > #foo:last-child { + font-size: 10px; +} + +#foo::before { + content: "Я последний"; +} + +#parent > #bar:first-child { + font-size: 20px; +} + +#bar::before { + content: "А я первый"; +} +``` + +
+
+ + **Сгенерированный HTML** + +```html +
+
+
+
+``` + +
+
+ +### JavaScript + +К сожалению, фреймворк не поддерживает JavaScript, но это не мешает подключить к нему внешний файл — `main.js`. Да и никто не запретит нам описывать селекторы для тегов `script` и добавлять туда атрибуты. + +```css +script#my-script[src="/main.js"] {} +``` + +В `main.js` можно использовать любой интерактив, который нужен сайту, — например, обработку событий, клиентский роутер и другие. + +## Заключение + +Возможно, некоторые уже захотели переписать свой продакшн-сайт на HTML in CSS, но мне жаль вас огорчать — на самом деле это фреймворк-шутка. + +Одним вечером мне стало интересно, как парсится CSS и как выглядит его структура. В процессе изучения пришла в голову идея создать свой фреймворк. Как говорится, закрепил теорию практикой. Кстати, фреймворк занимает около 120 строк неубористого кода. + + diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..5b5de2b --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /blog/ diff --git a/svelte.config.js b/svelte.config.js index b4b7de8..f42bf70 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,9 +1,14 @@ import adapter from '@sveltejs/adapter-node'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import {mdsvex} from "mdsvex"; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: vitePreprocess(), + extensions: ['.svelte', '.svx'], + preprocess: [ + vitePreprocess(), + mdsvex(), + ], kit: { adapter: adapter() } diff --git a/tailwind.config.js b/tailwind.config.js index 13207cc..c0a9789 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ['./src/**/*.{html,js,svelte,ts}'], + content: ['./src/**/*.{html,js,svelte,ts,svx}'], theme: { extend: {}, },