Skip to content

Commit

Permalink
Add redesign feature flag (#392)
Browse files Browse the repository at this point in the history
* Set up tailwindcss

* Set up theme

* Fix NpmRunBuild condition

* Remove condition again

* Add redesign feature flag and basic hljs setup

* Remove cssnano and fix package.json

* Revert

* Revert
  • Loading branch information
reakaleek authored Feb 3, 2025
1 parent 30905b4 commit 29118e1
Show file tree
Hide file tree
Showing 19 changed files with 1,775 additions and 104 deletions.
6 changes: 6 additions & 0 deletions src/Elastic.Markdown/.postcssrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": {
"@tailwindcss/postcss": {},
"postcss-import": {},
}
}
180 changes: 180 additions & 0 deletions src/Elastic.Markdown/Assets/hljs-merge-html-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
export const mergeHTMLPlugin = (function () {
'use strict';

var originalStream;

/**
* @param {string} value
* @returns {string}
*/
function escapeHTML(value) {
return value
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;');
}

/* plugin itself */

/** @type {HLJSPlugin} */
const mergeHTMLPlugin = {
// preserve the original HTML token stream
"before:highlightElement": ({ el }) => {
originalStream = nodeStream(el);
},
// merge it afterwards with the highlighted token stream
"after:highlightElement": ({ el, result, text }) => {
if (!originalStream.length) return;

const resultNode = document.createElement('div');
resultNode.innerHTML = result.value;
result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
el.innerHTML = result.value;
}
};

/* Stream merging support functions */

/**
* @typedef Event
* @property {'start'|'stop'} event
* @property {number} offset
* @property {Node} node
*/

/**
* @param {Node} node
*/
function tag(node) {
return node.nodeName.toLowerCase();
}

/**
* @param {Node} node
*/
function nodeStream(node) {
/** @type Event[] */
const result = [];
(function _nodeStream(node, offset) {
for (let child = node.firstChild; child; child = child.nextSibling) {
if (child.nodeType === 3) {
offset += child.nodeValue.length;
} else if (child.nodeType === 1) {
result.push({
event: 'start',
offset: offset,
node: child
});
offset = _nodeStream(child, offset);
// Prevent void elements from having an end tag that would actually
// double them in the output. There are more void elements in HTML
// but we list only those realistically expected in code display.
if (!tag(child).match(/br|hr|img|input/)) {
result.push({
event: 'stop',
offset: offset,
node: child
});
}
}
}
return offset;
})(node, 0);
return result;
}

/**
* @param {any} original - the original stream
* @param {any} highlighted - stream of the highlighted source
* @param {string} value - the original source itself
*/
function mergeStreams(original, highlighted, value) {
let processed = 0;
let result = '';
const nodeStack = [];

function selectStream() {
if (!original.length || !highlighted.length) {
return original.length ? original : highlighted;
}
if (original[0].offset !== highlighted[0].offset) {
return (original[0].offset < highlighted[0].offset) ? original : highlighted;
}

/*
To avoid starting the stream just before it should stop the order is
ensured that original always starts first and closes last:
if (event1 == 'start' && event2 == 'start')
return original;
if (event1 == 'start' && event2 == 'stop')
return highlighted;
if (event1 == 'stop' && event2 == 'start')
return original;
if (event1 == 'stop' && event2 == 'stop')
return highlighted;
... which is collapsed to:
*/
return highlighted[0].event === 'start' ? original : highlighted;
}

/**
* @param {Node} node
*/
function open(node) {
/** @param {Attr} attr */
function attributeString(attr) {
return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
}
// @ts-ignore
result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
}

/**
* @param {Node} node
*/
function close(node) {
result += '</' + tag(node) + '>';
}

/**
* @param {Event} event
*/
function render(event) {
(event.event === 'start' ? open : close)(event.node);
}

while (original.length || highlighted.length) {
let stream = selectStream();
result += escapeHTML(value.substring(processed, stream[0].offset));
processed = stream[0].offset;
if (stream === original) {
/*
On any opening or closing tag of the original markup we first close
the entire highlighted node stack, then render the original tag along
with all the following original tags at the same offset and then
reopen all the tags on the highlighted stack.
*/
nodeStack.reverse().forEach(close);
do {
render(stream.splice(0, 1)[0]);
stream = selectStream();
} while (stream === original && stream.length && stream[0].offset === processed);
nodeStack.reverse().forEach(open);
} else {
if (stream[0].event === 'start') {
nodeStack.push(stream[0].node);
} else {
nodeStack.pop();
}
render(stream.splice(0, 1)[0]);
}
}
return result + escapeHTML(value.substr(processed));
}

return mergeHTMLPlugin;
}());
18 changes: 18 additions & 0 deletions src/Elastic.Markdown/Assets/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import hljs from "highlight.js";
import {mergeHTMLPlugin} from "./hljs-merge-html-plugin";

hljs.registerLanguage('apiheader', function() {
return {
case_insensitive: true, // language is case-insensitive
keywords: 'GET POST PUT DELETE HEAD OPTIONS PATCH',
contains: [
hljs.HASH_COMMENT_MODE,
{
className: "subst", // (pathname: path1/path2/dothis) color #ab5656
begin: /(?<=(?:\/|GET |POST |PUT |DELETE |HEAD |OPTIONS |PATH))[^?\n\r\/]+/,
}
], }
})

hljs.addPlugin(mergeHTMLPlugin);
hljs.highlightAll();
29 changes: 29 additions & 0 deletions src/Elastic.Markdown/Assets/markdown/typography.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#elastic-docs-v3 {
h1 {
@apply text-4xl text-ink font-semibold mb-6;
line-height: 1.2em;
letter-spacing: -0.04em;
}

h2 {
@apply text-2xl text-ink font-bold mb-6;
line-height: 1.2em;
letter-spacing: -0.02em;
}

h3 {
@apply text-xl text-ink font-bold mb-6;
line-height: 1.2em;
letter-spacing: -0.02em;
}

p {
@apply text-base text-body mb-6;
line-height: 1.5em;
letter-spacing: 0;
}

a {
@apply text-blue-elastic hover:underline underline-offset-4;
}
}
16 changes: 8 additions & 8 deletions src/Elastic.Markdown/Assets/styles.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@import "legacy/pygments.css";
@import "legacy/shibuya.css";
@import "legacy/mystnb.css";
@import "legacy/copybutton.css";
@import "legacy/togglebutton.css";
@import "legacy/sphinx-design.min.css";
@import "legacy/custom.css";
@import "legacy/atom-one-light.css";
@import "tailwindcss";
@import "highlight.js/styles/atom-one-dark.css";
@import "./theme.css";
@import "./markdown/typography.css";

main.markdown-content {
max-width: 80ch;
}
44 changes: 44 additions & 0 deletions src/Elastic.Markdown/Assets/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@theme {
--color-*: initial;

--color-white: #FFFFFF;
--color-black: #000000;

--color-body: #515151;

--color-ink: #343741;
--color-ink-light: #535966;
--color-ink-dark: #1C1E23;

--color-gray: #E6EBF2;
--color-gray-light: #F5F7FA;
--color-gray-dark: #D4DAE5;

--color-blue-elastic: #0077CC;
--color-blue-sky: #36B9FF;
--color-blue-midnight: #20377D;

--color-red-light: #FB6363;
--color-red-dark: #D93333;

--color-green-light: #3CD278;
--color-green-dark: #148742;

--color-teal: #00BFB3;
--color-teal-light: #48EFCF;
--color-teal-dark: #00857F;

--color-poppy: #FA744E;
--color-poppy-light: #FF957D;
--color-poppy-dark: #E2543D;

--color-pink: #F04E98;
--color-pink-light: #F990C6;
--color-pink-dark: #DD0A73;

--color-yellow: #FEC514;
--color-yellow-light: #FFD836;
--color-yellow-dark: #F9B110;

--spacing: 4px;
}
21 changes: 14 additions & 7 deletions src/Elastic.Markdown/Slices/Layout/_Head.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@
<link rel="index" title="Index" href="@Model.Link("genindex.html")"/>
<link rel="search" title="Search" href="@Model.Link("search.html")"/>
<link rel="next" title="Elastic content" href="elastic/index.html"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("styles.css")"/>
<link media="print" rel="stylesheet" type="text/css" href="@Model.Static("print.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("pygments.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("shibuya.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("mystnb.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("copybutton.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("togglebutton.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("sphinx-design.min.css")"/>
<link media="print" rel="stylesheet" type="text/css" href="@Model.Static("/_static/print.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("custom.css")"/>
<link rel="stylesheet" type="text/css" href="@Model.Static("atom-one-light.css")"/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--sy-f-text: "Inter", var(--sy-f-sys), var(--sy-f-cjk), sans-serif;
--sy-f-heading: "Inter", var(--sy-f-sys), var(--sy-f-cjk), sans-serif;
}
</style>
:root {
--sy-f-text: "Inter", var(--sy-f-sys), var(--sy-f-cjk), sans-serif;
--sy-f-heading: "Inter", var(--sy-f-sys), var(--sy-f-cjk), sans-serif;
}
</style>
<meta property="og:type" content="website"/>
<meta property="og:title" content="Elastic Docs v3"/>
<meta name="twitter:card" content="summary"/>
Expand Down
30 changes: 29 additions & 1 deletion src/Elastic.Markdown/Slices/_Layout.cshtml
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
@inherits RazorLayoutSlice<LayoutViewModel>
@if (Model.IsRedesign)
{
<!DOCTYPE html>
<html lang="en">
<head>
<title>@Model.Title</title>
<link rel="stylesheet" type="text/css" href="@Model.Static("styles.css")"/>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="@(Model.AllowIndexing ? "index, follow" : "noindex, nofollow")">
</head>
<body class="text-ink flex flex-col min-h-screen">
<div id="elastic-nav"></div>
<script src='https://www.elastic.co/elastic-nav.js'></script>

<main class="markdown-content max-w-7xl mx-auto p-6 flex-1">
@await RenderBodyAsync()
</main>

<div id="elastic-footer"></div>
<script src='https://www.elastic.co/elastic-footer.js'></script>
<script src="@Model.Static("main.js")"></script>
</body>
</html>
}
else
{
<!DOCTYPE html>
<html lang="en" data-accent-color="blue" data-content_root="./">
@(await RenderPartialAsync(_Head.Create(Model)))
Expand Down Expand Up @@ -98,4 +125,5 @@
@await RenderSectionAsync("scripts")

</body>
</html>
</html>
}
2 changes: 2 additions & 0 deletions src/Elastic.Markdown/Slices/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class LayoutViewModel
public required string? GithubEditUrl { get; set; }
public required bool AllowIndexing { get; init; }

public bool IsRedesign => Environment.GetEnvironmentVariable("REDESIGN") == "true";

private MarkdownFile[]? _parents;
public MarkdownFile[] Parents
{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 29118e1

Please sign in to comment.