aboutsummaryrefslogtreecommitdiffstats
path: root/public/projects/js-small-apps/css-border-previewer
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-20 16:11:50 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-20 16:15:08 +0100
commit73a5c7fae9ffbe9ada721148c8c454a643aceebe (patch)
treec8fad013ed9b5dd589add87f8d45cf02bbfc6e91 /public/projects/js-small-apps/css-border-previewer
parentb01239fbdcc5bbc5921f73ec0e8fee7bedd5c8e8 (diff)
chore!: restructure repo
I separated public files from the config/dev files. It improves repo readability. I also moved dotenv helper to public/inc directory and extract the Matomo tracker in the same directory.
Diffstat (limited to 'public/projects/js-small-apps/css-border-previewer')
-rw-r--r--public/projects/js-small-apps/css-border-previewer/README.md13
-rw-r--r--public/projects/js-small-apps/css-border-previewer/app.js847
-rw-r--r--public/projects/js-small-apps/css-border-previewer/index.html625
-rw-r--r--public/projects/js-small-apps/css-border-previewer/style.css181
4 files changed, 1666 insertions, 0 deletions
diff --git a/public/projects/js-small-apps/css-border-previewer/README.md b/public/projects/js-small-apps/css-border-previewer/README.md
new file mode 100644
index 0000000..24a4566
--- /dev/null
+++ b/public/projects/js-small-apps/css-border-previewer/README.md
@@ -0,0 +1,13 @@
+# CSS Border Previewer
+
+An app to preview how a shapes looks when you change CSS border properties.
+
+You can find more details about the implementation here: https://github.com/florinpop17/app-ideas/blob/master/Projects/1-Beginner/Border-Radius-Previewer.md but I have decided to not limit myself to border-radius.
+
+## Preview
+
+You can see a live preview here: https://demo.armandphilippot.com/#css-border-previewer
+
+## License
+
+This project is open-source and available under the [MIT License](../LICENSE).
diff --git a/public/projects/js-small-apps/css-border-previewer/app.js b/public/projects/js-small-apps/css-border-previewer/app.js
new file mode 100644
index 0000000..583703d
--- /dev/null
+++ b/public/projects/js-small-apps/css-border-previewer/app.js
@@ -0,0 +1,847 @@
+/**
+ * Retrieve the border color property name depending on direction.
+ * @param {String} direction - Either `top`, `right`, `left` or `bottom`.
+ * @returns {String} The CSS property name.
+ */
+function getBorderColorProperty(direction) {
+ let borderColorProperty;
+
+ switch (direction) {
+ case "top":
+ borderColorProperty = "borderTopColor";
+ break;
+ case "right":
+ borderColorProperty = "borderRightColor";
+ break;
+ case "bottom":
+ borderColorProperty = "borderBottomColor";
+ break;
+ case "left":
+ borderColorProperty = "borderLeftColor";
+ break;
+ default:
+ borderColorProperty = "borderColor";
+ break;
+ }
+
+ return borderColorProperty;
+}
+
+/**
+ * Retrieve the border style property name depending on direction.
+ * @param {String} direction - Either `top`, `right`, `bottom` or `left`.
+ * @returns {String} The CSS property name.
+ */
+function getBorderStyleProperty(direction) {
+ let borderStyleProperty;
+
+ switch (direction) {
+ case "top":
+ borderStyleProperty = "borderTopStyle";
+ break;
+ case "right":
+ borderStyleProperty = "borderRightStyle";
+ break;
+ case "bottom":
+ borderStyleProperty = "borderBottomStyle";
+ break;
+ case "left":
+ borderStyleProperty = "borderLeftStyle";
+ break;
+ default:
+ borderStyleProperty = "borderStyle";
+ break;
+ }
+
+ return borderStyleProperty;
+}
+
+/**
+ * Retrieve the border width property name depending on direction.
+ * @param {String} direction - Either `top`, `right`, `left` or `bottom`.
+ * @returns {String} The CSS property name.
+ */
+function getBorderWidthProperty(direction) {
+ let borderWidthProperty;
+
+ switch (direction) {
+ case "top":
+ borderWidthProperty = "borderTopWidth";
+ break;
+ case "right":
+ borderWidthProperty = "borderRightWidth";
+ break;
+ case "bottom":
+ borderWidthProperty = "borderBottomWidth";
+ break;
+ case "left":
+ borderWidthProperty = "borderLeftWidth";
+ break;
+ default:
+ borderWidthProperty = "borderWidth";
+ break;
+ }
+
+ return borderWidthProperty;
+}
+
+/**
+ * Apply the custom border to an element.
+ * @param {HTMLElement} el - Apply border to this element.
+ * @param {String} property - Either `color`, `style` or `width`.
+ * @param {String} value - The value to apply.
+ * @param {String} [direction] - Either `top`, `right`, `bottom` or `left`.
+ */
+function setBorder(el, property, value, direction = null) {
+ let borderProperty;
+
+ switch (property) {
+ case "color":
+ borderProperty = getBorderColorProperty(direction);
+ break;
+ case "style":
+ borderProperty = getBorderStyleProperty(direction);
+ break;
+ case "width":
+ borderProperty = getBorderWidthProperty(direction);
+ default:
+ break;
+ }
+
+ el.style[borderProperty] = value;
+}
+
+/**
+ * Apply the custom border radius to an element.
+ * @param {HTMLElement} el - Apply border radius to this element.
+ * @param {String} firstRadius - The first radius value.
+ * @param {String} [secondRadius] - The second radius value.
+ * @param {String} [x] - The horizontal direction: either `right` or `left`.
+ * @param {String} [y] - The vertical direction: either `top` or `bottom`.
+ */
+function setBorderRadius(el, firstRadius, secondRadius, x = null, y = null) {
+ const direction = `${x}-${y}`;
+ const value = `${firstRadius}${secondRadius ? ` / ${secondRadius}` : ""}`;
+ let borderRadiusProperty;
+
+ switch (direction) {
+ case "left-top":
+ borderRadiusProperty = "borderTopLeftRadius";
+ break;
+ case "right-top":
+ borderRadiusProperty = "borderTopRightRadius";
+ break;
+ case "left-bottom":
+ borderRadiusProperty = "borderBottomLeftRadius";
+ break;
+ case "right-bottom":
+ borderRadiusProperty = "borderBottomRightRadius";
+ break;
+ default:
+ borderRadiusProperty = "borderRadius";
+ break;
+ }
+
+ el.style[borderRadiusProperty] = value;
+}
+
+/**
+ * Display the corresponding border settings.
+ * @param {String} string - Either `common` or `individual`.
+ */
+function toggleBorderSettingsDisplay(string) {
+ const allBordersFieldset = document.getElementById("fieldset-borders");
+ const topBorderFieldset = document.getElementById("fieldset-border-top");
+ const rightBorderFieldset = document.getElementById("fieldset-border-right");
+ const bottomBorderFieldset = document.getElementById(
+ "fieldset-border-bottom"
+ );
+ const leftBorderFieldset = document.getElementById("fieldset-border-left");
+
+ if (string === "common") {
+ allBordersFieldset.style.display = "";
+ topBorderFieldset.style.display = "none";
+ rightBorderFieldset.style.display = "none";
+ bottomBorderFieldset.style.display = "none";
+ leftBorderFieldset.style.display = "none";
+ } else {
+ allBordersFieldset.style.display = "none";
+ topBorderFieldset.style.display = "";
+ rightBorderFieldset.style.display = "";
+ bottomBorderFieldset.style.display = "";
+ leftBorderFieldset.style.display = "";
+ }
+}
+
+/**
+ * Display the corresponding border-radius settings.
+ * @param {String} string - Either `common` or `individual`.
+ */
+function toggleBorderRadiusSettingsDisplay(string) {
+ const allBordersRadiusFieldset = document.getElementById(
+ "fieldset-borders-radius"
+ );
+ const topLeftBorderRadiusFieldset = document.getElementById(
+ "fieldset-border-top-left-radius"
+ );
+ const topRightBorderRadiusFieldset = document.getElementById(
+ "fieldset-border-top-right-radius"
+ );
+ const bottomLeftBorderRadiusFieldset = document.getElementById(
+ "fieldset-border-bottom-left-radius"
+ );
+ const bottomRightBorderRadiusFieldset = document.getElementById(
+ "fieldset-border-bottom-right-radius"
+ );
+
+ if (string === "common") {
+ allBordersRadiusFieldset.style.display = "";
+ topLeftBorderRadiusFieldset.style.display = "none";
+ topRightBorderRadiusFieldset.style.display = "none";
+ bottomLeftBorderRadiusFieldset.style.display = "none";
+ bottomRightBorderRadiusFieldset.style.display = "none";
+ } else {
+ allBordersRadiusFieldset.style.display = "none";
+ topLeftBorderRadiusFieldset.style.display = "";
+ topRightBorderRadiusFieldset.style.display = "";
+ bottomLeftBorderRadiusFieldset.style.display = "";
+ bottomRightBorderRadiusFieldset.style.display = "";
+ }
+}
+
+/**
+ * Print the generated code into the given element.
+ * @param {HTMLElement} el - The element where to print generated code.
+ */
+function printCode(el) {
+ const code = document.querySelector(".result__code");
+ let codeOutput = `
+.box {\n`;
+
+ for (const property of el.style) {
+ codeOutput += `\t${property}: ${el.style[property]};\n`;
+ }
+
+ codeOutput += "}";
+ code.textContent = codeOutput;
+}
+
+/**
+ * Check which type of settings is checked.
+ * @param {String} radioValue - The input radio value.
+ * @returns {Boolean} True if is individual; false if is common.
+ */
+function isIndividualSettings(radioValue) {
+ return radioValue === "true" ? true : false;
+}
+
+/**
+ * Set all borders to a given element.
+ * @param {HTMLElement} el - Apply border to this element.
+ */
+function setCommonBorder(el) {
+ const allBordersColorInput = document.getElementById("borders-color");
+ const allBordersStyleSelect = document.getElementById("borders-style");
+ const allBordersUnitSelect = document.getElementById("borders-unit");
+ const allBordersWidthInput = document.getElementById("borders-width");
+
+ setBorder(el, "color", allBordersColorInput.value);
+ setBorder(el, "style", allBordersStyleSelect.value);
+ setBorder(
+ el,
+ "width",
+ `${allBordersWidthInput.value}${allBordersUnitSelect.value}`
+ );
+
+ allBordersColorInput.addEventListener("input", () => {
+ setBorder(el, "color", allBordersColorInput.value);
+ printCode(el);
+ });
+
+ allBordersStyleSelect.addEventListener("input", () => {
+ setBorder(el, "style", allBordersStyleSelect.value);
+ printCode(el);
+ });
+
+ allBordersUnitSelect.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${allBordersWidthInput.value}${allBordersUnitSelect.value}`
+ );
+ printCode(el);
+ });
+
+ allBordersWidthInput.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${allBordersWidthInput.value}${allBordersUnitSelect.value}`
+ );
+ printCode(el);
+ });
+}
+
+/**
+ * Set the top border to the given element.
+ * @param {HTMLElement} el - Apply the top border to this element.
+ */
+function setTopBorder(el) {
+ const topBorderColorInput = document.getElementById("border-top-color");
+ const topBorderStyleSelect = document.getElementById("border-top-style");
+ const topBorderUnitSelect = document.getElementById("border-top-unit");
+ const topBorderWidthInput = document.getElementById("border-top-width");
+
+ setBorder(el, "color", topBorderColorInput.value, "top");
+ setBorder(el, "style", topBorderStyleSelect.value, "top");
+ setBorder(
+ el,
+ "width",
+ `${topBorderWidthInput.value}${topBorderUnitSelect.value}`,
+ "top"
+ );
+
+ topBorderColorInput.addEventListener("input", () => {
+ setBorder(el, "color", topBorderColorInput.value, "top");
+ printCode(el);
+ });
+
+ topBorderStyleSelect.addEventListener("input", () => {
+ setBorder(el, "style", topBorderStyleSelect.value, "top");
+ printCode(el);
+ });
+
+ topBorderUnitSelect.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${topBorderWidthInput.value}${topBorderUnitSelect.value}`,
+ "top"
+ );
+ printCode(el);
+ });
+
+ topBorderWidthInput.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${topBorderWidthInput.value}${topBorderUnitSelect.value}`,
+ "top"
+ );
+ printCode(el);
+ });
+}
+
+/**
+ * Set the right border to the given element.
+ * @param {HTMLElement} el - Apply the right border to this element.
+ */
+function setRightBorder(el) {
+ const rightBorderWidthInput = document.getElementById("border-right-width");
+ const rightBorderUnitSelect = document.getElementById("border-right-unit");
+ const rightBorderStyleSelect = document.getElementById("border-right-style");
+ const rightBorderColorInput = document.getElementById("border-right-color");
+
+ setBorder(el, "color", rightBorderColorInput.value, "right");
+ setBorder(el, "style", rightBorderStyleSelect.value, "right");
+ setBorder(
+ el,
+ "width",
+ `${rightBorderWidthInput.value}${rightBorderUnitSelect.value}`,
+ "right"
+ );
+
+ rightBorderColorInput.addEventListener("input", () => {
+ setBorder(el, "color", rightBorderColorInput.value, "right");
+ printCode(el);
+ });
+
+ rightBorderStyleSelect.addEventListener("input", () => {
+ setBorder(el, "style", rightBorderStyleSelect.value, "right");
+ printCode(el);
+ });
+
+ rightBorderUnitSelect.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${rightBorderWidthInput.value}${rightBorderUnitSelect.value}`,
+ "right"
+ );
+ printCode(el);
+ });
+
+ rightBorderWidthInput.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${rightBorderWidthInput.value}${rightBorderUnitSelect.value}`,
+ "right"
+ );
+ printCode(el);
+ });
+}
+
+/**
+ * Set the bottom border to the given element.
+ * @param {HTMLElement} el - Apply the bottom border to this element.
+ */
+function setBottomBorder(el) {
+ const bottomBorderWidthInput = document.getElementById("border-bottom-width");
+ const bottomBorderUnitSelect = document.getElementById("border-bottom-unit");
+ const bottomBorderStyleSelect = document.getElementById(
+ "border-bottom-style"
+ );
+ const bottomBorderColorInput = document.getElementById("border-bottom-color");
+
+ setBorder(el, "color", bottomBorderColorInput.value, "bottom");
+ setBorder(el, "style", bottomBorderStyleSelect.value, "bottom");
+ setBorder(
+ el,
+ "width",
+ `${bottomBorderWidthInput.value}${bottomBorderUnitSelect.value}`,
+ "bottom"
+ );
+
+ bottomBorderColorInput.addEventListener("input", () => {
+ setBorder(el, "color", bottomBorderColorInput.value, "bottom");
+ printCode(el);
+ });
+
+ bottomBorderStyleSelect.addEventListener("input", () => {
+ setBorder(el, "style", bottomBorderStyleSelect.value, "bottom");
+ printCode(el);
+ });
+
+ bottomBorderUnitSelect.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${bottomBorderWidthInput.value}${bottomBorderUnitSelect.value}`,
+ "bottom"
+ );
+ printCode(el);
+ });
+
+ bottomBorderWidthInput.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${bottomBorderWidthInput.value}${bottomBorderUnitSelect.value}`,
+ "bottom"
+ );
+ printCode(el);
+ });
+}
+
+/**
+ * Set the left border to the given element.
+ * @param {HTMLElement} el - Apply the left border to this element.
+ */
+function setLeftBorder(el) {
+ const leftBorderWidthInput = document.getElementById("border-left-width");
+ const leftBorderUnitSelect = document.getElementById("border-left-unit");
+ const leftBorderStyleSelect = document.getElementById("border-left-style");
+ const leftBorderColorInput = document.getElementById("border-left-color");
+
+ setBorder(el, "color", leftBorderColorInput.value, "left");
+ setBorder(el, "style", leftBorderStyleSelect.value, "left");
+ setBorder(
+ el,
+ "width",
+ `${leftBorderWidthInput.value}${leftBorderUnitSelect.value}`,
+ "left"
+ );
+
+ leftBorderColorInput.addEventListener("input", () => {
+ setBorder(el, "color", leftBorderColorInput.value, "left");
+ printCode(el);
+ });
+
+ leftBorderStyleSelect.addEventListener("input", () => {
+ setBorder(el, "style", leftBorderStyleSelect.value, "left");
+ printCode(el);
+ });
+
+ leftBorderUnitSelect.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${leftBorderWidthInput.value}${leftBorderUnitSelect.value}`,
+ "left"
+ );
+ printCode(el);
+ });
+
+ leftBorderWidthInput.addEventListener("input", () => {
+ setBorder(
+ el,
+ "width",
+ `${leftBorderWidthInput.value}${leftBorderUnitSelect.value}`,
+ "left"
+ );
+ printCode(el);
+ });
+}
+
+/**
+ * Set all borders radius to the given element.
+ * @param {HTMLElement} el - Apply the border radius to this element.
+ */
+function setCommonBorderRadius(el) {
+ const borderCommonFirstRadius = document.getElementById(
+ "borders-first-radius"
+ );
+ const borderCommonFirstRadiusUnit = document.getElementById(
+ "borders-first-radius-unit"
+ );
+ const borderCommonSecondRadius = document.getElementById(
+ "borders-second-radius"
+ );
+ const borderCommonSecondRadiusUnit = document.getElementById(
+ "borders-second-radius-unit"
+ );
+ let firstRadius = `${borderCommonFirstRadius.value}${borderCommonFirstRadiusUnit.value}`;
+ let secondRadius = borderCommonSecondRadius.value
+ ? `${borderCommonSecondRadius.value}${borderCommonSecondRadiusUnit.value}`
+ : null;
+
+ setBorderRadius(el, firstRadius, secondRadius);
+
+ borderCommonFirstRadius.addEventListener("input", () => {
+ firstRadius = `${borderCommonFirstRadius.value}${borderCommonFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius);
+ printCode(el);
+ });
+
+ borderCommonFirstRadiusUnit.addEventListener("input", () => {
+ firstRadius = `${borderCommonFirstRadius.value}${borderCommonFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius);
+ printCode(el);
+ });
+
+ borderCommonSecondRadius.addEventListener("input", () => {
+ secondRadius = borderCommonSecondRadius.value
+ ? `${borderCommonSecondRadius.value}${borderCommonSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius);
+ printCode(el);
+ });
+
+ borderCommonSecondRadiusUnit.addEventListener("input", () => {
+ secondRadius = borderCommonSecondRadius.value
+ ? `${borderCommonSecondRadius.value}${borderCommonSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius);
+ printCode(el);
+ });
+}
+
+/**
+ * Set the top left border-radius to the given element.
+ * @param {HTMLElement} el - Apply the top left border-radius to this element.
+ */
+function setTopLeftBorderRadius(el) {
+ const borderTopLeftFirstRadius = document.getElementById(
+ "border-top-left-first-radius"
+ );
+ const borderTopLeftFirstRadiusUnit = document.getElementById(
+ "border-top-left-first-radius-unit"
+ );
+ const borderTopLeftSecondRadius = document.getElementById(
+ "border-top-left-second-radius"
+ );
+ const borderTopLeftSecondRadiusUnit = document.getElementById(
+ "border-top-left-second-radius-unit"
+ );
+ let firstRadius = `${borderTopLeftFirstRadius.value}${borderTopLeftFirstRadiusUnit.value}`;
+ let secondRadius = borderTopLeftSecondRadius.value
+ ? `${borderTopLeftSecondRadius.value}${borderTopLeftSecondRadiusUnit.value}`
+ : null;
+
+ setBorderRadius(el, firstRadius, secondRadius, "left", "top");
+
+ borderTopLeftFirstRadius.addEventListener("input", () => {
+ firstRadius = `${borderTopLeftFirstRadius.value}${borderTopLeftFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "top");
+ printCode(el);
+ });
+
+ borderTopLeftFirstRadiusUnit.addEventListener("input", () => {
+ firstRadius = `${borderTopLeftFirstRadius.value}${borderTopLeftFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "top");
+ printCode(el);
+ });
+
+ borderTopLeftSecondRadius.addEventListener("input", () => {
+ secondRadius = borderTopLeftSecondRadius.value
+ ? `${borderTopLeftSecondRadius.value}${borderTopLeftSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "top");
+ printCode(el);
+ });
+
+ borderTopLeftSecondRadiusUnit.addEventListener("input", () => {
+ secondRadius = borderTopLeftSecondRadius.value
+ ? `${borderTopLeftSecondRadius.value}${borderTopLeftSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "top");
+ printCode(el);
+ });
+}
+
+/**
+ * Set the top right border-radius to the given element.
+ * @param {HTMLElement} el - Apply the top right border-radius to this element.
+ */
+function setTopRightBorderRadius(el) {
+ const borderTopRightFirstRadius = document.getElementById(
+ "border-top-right-first-radius"
+ );
+ const borderTopRightFirstRadiusUnit = document.getElementById(
+ "border-top-right-first-radius-unit"
+ );
+ const borderTopRightSecondRadius = document.getElementById(
+ "border-top-right-second-radius"
+ );
+ const borderTopRightSecondRadiusUnit = document.getElementById(
+ "border-top-right-second-radius-unit"
+ );
+ const firstRadius = `${borderTopRightFirstRadius.value}${borderTopRightFirstRadiusUnit.value}`;
+ const secondRadius = borderTopRightSecondRadius.value
+ ? `${borderTopRightSecondRadius.value}${borderTopRightSecondRadiusUnit.value}`
+ : null;
+
+ setBorderRadius(el, firstRadius, secondRadius, "right", "top");
+
+ borderTopRightFirstRadius.addEventListener("input", () => {
+ firstRadius = `${borderTopRightFirstRadius.value}${borderTopRightFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "top");
+ printCode(el);
+ });
+
+ borderTopRightFirstRadiusUnit.addEventListener("input", () => {
+ firstRadius = `${borderTopRightFirstRadius.value}${borderTopRightFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "top");
+ printCode(el);
+ });
+
+ borderTopRightSecondRadius.addEventListener("input", () => {
+ secondRadius = borderTopRightSecondRadius.value
+ ? `${borderTopRightSecondRadius.value}${borderTopRightSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "top");
+ printCode(el);
+ });
+
+ borderTopRightSecondRadiusUnit.addEventListener("input", () => {
+ secondRadius = borderTopRightSecondRadius.value
+ ? `${borderTopRightSecondRadius.value}${borderTopRightSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "top");
+ printCode(el);
+ });
+}
+
+/**
+ * Set the bottom left border-radius to the given element.
+ * @param {HTMLElement} el - Apply bottom left border-radius to this element.
+ */
+function setBottomLeftBorderRadius(el) {
+ const borderBottomLeftFirstRadius = document.getElementById(
+ "border-bottom-left-first-radius"
+ );
+ const borderBottomLeftFirstRadiusUnit = document.getElementById(
+ "border-bottom-left-first-radius-unit"
+ );
+ const borderBottomLeftSecondRadius = document.getElementById(
+ "border-bottom-left-second-radius"
+ );
+ const borderBottomLeftSecondRadiusUnit = document.getElementById(
+ "border-bottom-left-second-radius-unit"
+ );
+ const firstRadius = `${borderBottomLeftFirstRadius.value}${borderBottomLeftFirstRadiusUnit.value}`;
+ const secondRadius = borderBottomLeftSecondRadius.value
+ ? `${borderBottomLeftSecondRadius.value}${borderBottomLeftSecondRadiusUnit.value}`
+ : null;
+
+ setBorderRadius(el, firstRadius, secondRadius, "left", "bottom");
+
+ borderBottomLeftFirstRadius.addEventListener("input", () => {
+ firstRadius = `${borderBottomLeftFirstRadius.value}${borderBottomLeftFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "bottom");
+ printCode(el);
+ });
+
+ borderBottomLeftFirstRadiusUnit.addEventListener("input", () => {
+ firstRadius = `${borderBottomLeftFirstRadius.value}${borderBottomLeftFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "bottom");
+ printCode(el);
+ });
+
+ borderBottomLeftSecondRadius.addEventListener("input", () => {
+ secondRadius = borderBottomLeftSecondRadius.value
+ ? `${borderBottomLeftSecondRadius.value}${borderBottomLeftSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "bottom");
+ printCode(el);
+ });
+
+ borderBottomLeftSecondRadiusUnit.addEventListener("input", () => {
+ secondRadius = borderBottomLeftSecondRadius.value
+ ? `${borderBottomLeftSecondRadius.value}${borderBottomLeftSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "left", "bottom");
+ printCode(el);
+ });
+}
+
+/**
+ * Set the bottom right border-radius to the given element.
+ * @param {HTMLElement} el - Apply bottom right border-radius to this element.
+ */
+function setBottomRightBorderRadius(el) {
+ const borderBottomRightFirstRadius = document.getElementById(
+ "border-bottom-right-first-radius"
+ );
+ const borderBottomRightFirstRadiusUnit = document.getElementById(
+ "border-bottom-right-first-radius-unit"
+ );
+ const borderBottomRightSecondRadius = document.getElementById(
+ "border-bottom-right-second-radius"
+ );
+ const borderBottomRightSecondRadiusUnit = document.getElementById(
+ "border-bottom-right-second-radius-unit"
+ );
+ const firstRadius = `${borderBottomRightFirstRadius.value}${borderBottomRightFirstRadiusUnit.value}`;
+ const secondRadius = borderBottomRightSecondRadius.value
+ ? `${borderBottomRightSecondRadius.value}${borderBottomRightSecondRadiusUnit.value}`
+ : null;
+
+ setBorderRadius(el, firstRadius, secondRadius, "right", "bottom");
+
+ borderBottomRightFirstRadius.addEventListener("input", () => {
+ firstRadius = `${borderBottomRightFirstRadius.value}${borderBottomRightFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "bottom");
+ printCode(el);
+ });
+
+ borderBottomRightFirstRadiusUnit.addEventListener("input", () => {
+ firstRadius = `${borderBottomRightFirstRadius.value}${borderBottomRightFirstRadiusUnit.value}`;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "bottom");
+ printCode(el);
+ });
+
+ borderBottomRightSecondRadius.addEventListener("input", () => {
+ secondRadius = borderBottomRightSecondRadius.value
+ ? `${borderBottomRightSecondRadius.value}${borderBottomRightSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "bottom");
+ printCode(el);
+ });
+
+ borderBottomRightSecondRadiusUnit.addEventListener("input", () => {
+ secondRadius = borderBottomRightSecondRadius.value
+ ? `${borderBottomRightSecondRadius.value}${borderBottomRightSecondRadiusUnit.value}`
+ : null;
+ setBorderRadius(el, firstRadius, secondRadius, "right", "bottom");
+ printCode(el);
+ });
+}
+
+/**
+ * Display a message inside the given element.
+ * @param {HTMLElement} el - The element where to print the message.
+ * @param {String} msg - The message to display.
+ * @param {Number} [duration] - The message duration.
+ */
+function printMessage(el, msg, duration = 1000) {
+ const backupContent = el.textContent;
+
+ el.textContent = msg;
+ setTimeout(() => (el.textContent = backupContent), duration);
+}
+
+/**
+ * Copy code to the clipboard.
+ */
+function copyCode() {
+ const code = document.querySelector(".result__code");
+ navigator.clipboard.writeText(code.textContent);
+}
+
+/**
+ * Listen the button copy to clipboard.
+ */
+function listenCopyCodeBtn() {
+ const btn = document.getElementById("copy-code");
+
+ btn.addEventListener("click", () => {
+ copyCode();
+ printMessage(btn, "Copied to clipboard!");
+ });
+}
+
+/**
+ * Initialize borders settings and borders.
+ * @param {String} radioValue - The input radio value.
+ * @param {HTMLElement} el - The element where to apply borders.
+ */
+function initBorders(radioValue, el) {
+ if (isIndividualSettings(radioValue)) {
+ toggleBorderSettingsDisplay("individual");
+ setTopBorder(el);
+ setRightBorder(el);
+ setBottomBorder(el);
+ setLeftBorder(el);
+ } else {
+ toggleBorderSettingsDisplay("common");
+ setCommonBorder(el);
+ }
+}
+
+/**
+ * Initialize border-radius settings and border-radius.
+ * @param {String} radioValue - The input radio value.
+ * @param {HTMLElement} el - The element where to apply border-radius.
+ */
+function initBordersRadius(radioValue, el) {
+ if (isIndividualSettings(radioValue)) {
+ toggleBorderRadiusSettingsDisplay("individual");
+ setTopLeftBorderRadius(el);
+ setTopRightBorderRadius(el);
+ setBottomLeftBorderRadius(el);
+ setBottomRightBorderRadius(el);
+ } else {
+ toggleBorderRadiusSettingsDisplay("common");
+ setCommonBorderRadius(el);
+ }
+}
+
+/**
+ * Initialize the app.
+ */
+function init() {
+ const box = document.querySelector(".box");
+ const borderPropertyRadio = document.querySelectorAll(
+ 'input[name="border-property"]'
+ );
+ const borderRadiusPropertyRadio = document.querySelectorAll(
+ 'input[name="border-radius-property"]'
+ );
+
+ for (const radio of borderPropertyRadio) {
+ if (radio.checked) initBorders(radio.value, box);
+ radio.addEventListener("change", () => initBorders(radio.value, box));
+ }
+
+ for (const radio of borderRadiusPropertyRadio) {
+ if (radio.checked) initBordersRadius(radio.value, box);
+ radio.addEventListener("change", () => initBordersRadius(radio.value, box));
+ }
+
+ printCode(box);
+ listenCopyCodeBtn();
+}
+
+init();
diff --git a/public/projects/js-small-apps/css-border-previewer/index.html b/public/projects/js-small-apps/css-border-previewer/index.html
new file mode 100644
index 0000000..bf16fa5
--- /dev/null
+++ b/public/projects/js-small-apps/css-border-previewer/index.html
@@ -0,0 +1,625 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>CSS Border Previewer</title>
+ <link rel="stylesheet" href="style.css" />
+ </head>
+ <body>
+ <header class="header">
+ <h1 class="branding">CSS Border Previewer</h1>
+ </header>
+ <main class="main">
+ <div class="preview">
+ <div class="box"></div>
+ </div>
+ <form action="#" method="POST" class="settings form">
+ <fieldset class="form__fieldset">
+ <legend class="form__legend">Global settings</legend>
+ <div class="form__item">
+ Control border property separately?
+ <input
+ type="radio"
+ name="border-property"
+ id="border-property-no"
+ value="false"
+ checked
+ />
+ <label for="border-property-no">No</label>
+ <input
+ type="radio"
+ name="border-property"
+ id="border-property-yes"
+ value="true"
+ />
+ <label for="border-property-yes">Yes</label>
+ </div>
+ <div class="form__item">
+ Control border radius property separately?
+ <input
+ type="radio"
+ name="border-radius-property"
+ id="border-radius-property-no"
+ value="false"
+ checked
+ />
+ <label for="border-radius-property-no">No</label>
+ <input
+ type="radio"
+ name="border-radius-property"
+ id="border-radius-property-yes"
+ value="true"
+ />
+ <label for="border-radius-property-yes">Yes</label>
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-borders"
+ class="form__fieldset form__fieldset--flex"
+ >
+ <legend class="form__legend">Borders settings</legend>
+ <div class="form__item form__item--flex">
+ <label for="borders-width">Width</label>
+ <input
+ type="number"
+ name="borders-width"
+ id="borders-width"
+ size="3"
+ value="1"
+ class="form__input"
+ />
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="borders-unit" class="form__label">Unit</label>
+ <select name="borders-unit" id="borders-unit" class="form__select">
+ <option value="px" selected>px</option>
+ <option value="cm">cm</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="borders-style">Style</label>
+ <select
+ name="borders-style"
+ id="borders-style"
+ class="form__select"
+ >
+ <option value="none">None</option>
+ <option value="dashed">Dashed</option>
+ <option value="dotted">Dotted</option>
+ <option value="double">Double</option>
+ <option value="groove">Groove</option>
+ <option value="hidden">Hidden</option>
+ <option value="inset">Inset</option>
+ <option value="outset">Outset</option>
+ <option value="ridge">Ridge</option>
+ <option value="solid" selected>Solid</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="borders-color">Color</label>
+ <input
+ type="color"
+ name="borders-color"
+ id="borders-color"
+ value="#000000"
+ class="form__input form__input--color"
+ />
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-top"
+ class="form__fieldset form__fieldset--flex"
+ >
+ <legend class="form__legend">Border-top settings</legend>
+ <div class="form__item form__item--flex">
+ <label for="border-top-width">Width</label>
+ <input
+ type="number"
+ name="border-top-width"
+ id="border-top-width"
+ size="3"
+ value="1"
+ class="form__input"
+ />
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-top-unit" class="form__label">Unit</label>
+ <select
+ name="border-top-unit"
+ id="border-top-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="cm">cm</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-top-style">Style</label>
+ <select
+ name="border-top-style"
+ id="border-top-style"
+ class="form__select"
+ >
+ <option value="none">None</option>
+ <option value="dashed">Dashed</option>
+ <option value="dotted">Dotted</option>
+ <option value="double">Double</option>
+ <option value="groove">Groove</option>
+ <option value="hidden">Hidden</option>
+ <option value="inset">Inset</option>
+ <option value="outset">Outset</option>
+ <option value="ridge">Ridge</option>
+ <option value="solid" selected>Solid</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-top-color">Color</label>
+ <input
+ type="color"
+ name="border-top-color"
+ id="border-top-color"
+ value="#000000"
+ class="form__input form__input--color"
+ />
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-right"
+ class="form__fieldset form__fieldset--flex"
+ >
+ <legend class="form__legend">Border-right settings</legend>
+ <div class="form__item form__item--flex">
+ <label for="border-right-width">Width</label>
+ <input
+ type="number"
+ name="border-right-width"
+ id="border-right-width"
+ size="3"
+ value="1"
+ class="form__input"
+ />
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-right-unit" class="form__label">Unit</label>
+ <select
+ name="border-right-unit"
+ id="border-right-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="cm">cm</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-right-style">Style</label>
+ <select
+ name="border-right-style"
+ id="border-right-style"
+ class="form__select"
+ >
+ <option value="none">None</option>
+ <option value="dashed">Dashed</option>
+ <option value="dotted">Dotted</option>
+ <option value="double">Double</option>
+ <option value="groove">Groove</option>
+ <option value="hidden">Hidden</option>
+ <option value="inset">Inset</option>
+ <option value="outset">Outset</option>
+ <option value="ridge">Ridge</option>
+ <option value="solid" selected>Solid</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-right-color">Color</label>
+ <input
+ type="color"
+ name="border-right-color"
+ id="border-right-color"
+ value="#000000"
+ class="form__input form__input--color"
+ />
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-bottom"
+ class="form__fieldset form__fieldset--flex"
+ >
+ <legend class="form__legend">Border-bottom settings</legend>
+ <div class="form__item form__item--flex">
+ <label for="border-bottom-width">Width</label>
+ <input
+ type="number"
+ name="border-bottom-width"
+ id="border-bottom-width"
+ size="3"
+ value="1"
+ class="form__input"
+ />
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-bottom-unit" class="form__label">Unit</label>
+ <select
+ name="border-bottom-unit"
+ id="border-bottom-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="cm">cm</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-bottom-style">Style</label>
+ <select
+ name="border-bottom-style"
+ id="border-bottom-style"
+ class="form__select"
+ >
+ <option value="none">None</option>
+ <option value="dashed">Dashed</option>
+ <option value="dotted">Dotted</option>
+ <option value="double">Double</option>
+ <option value="groove">Groove</option>
+ <option value="hidden">Hidden</option>
+ <option value="inset">Inset</option>
+ <option value="outset">Outset</option>
+ <option value="ridge">Ridge</option>
+ <option value="solid" selected>Solid</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-bottom-color">Color</label>
+ <input
+ type="color"
+ name="border-bottom-color"
+ id="border-bottom-color"
+ value="#000000"
+ class="form__input form__input--color"
+ />
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-left"
+ class="form__fieldset form__fieldset--flex"
+ >
+ <legend class="form__legend">Border-left settings</legend>
+ <div class="form__item form__item--flex">
+ <label for="border-left-width">Width</label>
+ <input
+ type="number"
+ name="border-left-width"
+ id="border-left-width"
+ size="3"
+ value="1"
+ class="form__input"
+ />
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-left-unit" class="form__label">Unit</label>
+ <select
+ name="border-left-unit"
+ id="border-left-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="cm">cm</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-left-style">Style</label>
+ <select
+ name="border-left-style"
+ id="border-left-style"
+ class="form__select"
+ >
+ <option value="none">None</option>
+ <option value="dashed">Dashed</option>
+ <option value="dotted">Dotted</option>
+ <option value="double">Double</option>
+ <option value="groove">Groove</option>
+ <option value="hidden">Hidden</option>
+ <option value="inset">Inset</option>
+ <option value="outset">Outset</option>
+ <option value="ridge">Ridge</option>
+ <option value="solid" selected>Solid</option>
+ </select>
+ </div>
+ <div class="form__item form__item--flex">
+ <label for="border-left-color">Color</label>
+ <input
+ type="color"
+ name="border-left-color"
+ id="border-left-color"
+ value="#000000"
+ class="form__input form__input--color"
+ />
+ </div>
+ </fieldset>
+ <fieldset id="fieldset-borders-radius" class="form__fieldset">
+ <legend class="form__legend">Border-radius settings</legend>
+ <div class="form__item">
+ <p>First radius:</p>
+ <label for="borders-first-radius">Value</label>
+ <input
+ type="number"
+ name="borders-first-radius"
+ id="borders-first-radius"
+ class="form__input"
+ />
+ <label for="borders-first-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="borders-first-radius-unit"
+ id="borders-first-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item">
+ <p>Second radius:</p>
+ <label for="borders-second-radius">Value</label>
+ <input
+ type="number"
+ name="borders-second-radius"
+ id="borders-second-radius"
+ class="form__input"
+ />
+ <label for="borders-second-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="borders-second-radius-unit"
+ id="borders-second-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ </fieldset>
+ <fieldset id="fieldset-border-top-left-radius" class="form__fieldset">
+ <legend class="form__legend">Border-top-left-radius settings</legend>
+ <div class="form__item">
+ <p>First radius:</p>
+ <label for="border-top-left-first-radius">Value</label>
+ <input
+ type="number"
+ name="border-top-left-first-radius"
+ id="border-top-left-first-radius"
+ class="form__input"
+ />
+ <label for="border-top-left-first-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-top-left-first-radius-unit"
+ id="border-top-left-first-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item">
+ <p>Second radius:</p>
+ <label for="border-top-left-second-radius">Value</label>
+ <input
+ type="number"
+ name="border-top-left-second-radius"
+ id="border-top-left-second-radius"
+ class="form__input"
+ />
+ <label for="border-top-left-second-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-top-left-second-radius-unit"
+ id="border-top-left-second-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ </fieldset>
+ <fieldset id="fieldset-border-top-right-radius" class="form__fieldset">
+ <legend class="form__legend">Border-top-right-radius settings</legend>
+ <div class="form__item">
+ <p>First radius:</p>
+ <label for="border-top-right-first-radius">Value</label>
+ <input
+ type="number"
+ name="border-top-right-first-radius"
+ id="border-top-right-first-radius"
+ class="form__input"
+ />
+ <label for="border-top-right-first-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-top-right-first-radius-unit"
+ id="border-top-right-first-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item">
+ <p>Second radius:</p>
+ <label for="border-top-right-second-radius">Value</label>
+ <input
+ type="number"
+ name="border-top-right-second-radius"
+ id="border-top-right-second-radius"
+ class="form__input"
+ />
+ <label for="border-top-right-second-radius-unit" class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-top-right-second-radius-unit"
+ id="border-top-right-second-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-bottom-left-radius"
+ class="form__fieldset"
+ >
+ <legend class="form__legend">
+ Border-bottom-left-radius settings
+ </legend>
+ <div class="form__item">
+ <p>First radius:</p>
+ <label for="border-bottom-left-first-radius">Value</label>
+ <input
+ type="number"
+ name="border-bottom-left-first-radius"
+ id="border-bottom-left-first-radius"
+ class="form__input"
+ />
+ <label
+ for="border-bottom-left-first-radius-unit"
+ class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-bottom-left-first-radius-unit"
+ id="border-bottom-left-first-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item">
+ <p>Second radius:</p>
+ <label for="border-bottom-left-second-radius">Value</label>
+ <input
+ type="number"
+ name="border-bottom-left-second-radius"
+ id="border-bottom-left-second-radius"
+ class="form__input"
+ />
+ <label
+ for="border-bottom-left-second-radius-unit"
+ class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-bottom-left-second-radius-unit"
+ id="border-bottom-left-second-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ </fieldset>
+ <fieldset
+ id="fieldset-border-bottom-right-radius"
+ class="form__fieldset"
+ >
+ <legend class="form__legend">
+ Border-bottom-right-radius settings
+ </legend>
+ <div class="form__item">
+ <p>First radius:</p>
+ <label for="border-bottom-right-first-radius">Value</label>
+ <input
+ type="number"
+ name="border-bottom-right-first-radius"
+ id="border-bottom-right-first-radius"
+ class="form__input"
+ />
+ <label
+ for="border-bottom-right-first-radius-unit"
+ class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-bottom-right-first-radius-unit"
+ id="border-bottom-right-first-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ <div class="form__item">
+ <p>Second radius:</p>
+ <label for="border-bottom-right-second-radius">Value</label>
+ <input
+ type="number"
+ name="border-bottom-right-second-radius"
+ id="border-bottom-right-second-radius"
+ class="form__input"
+ />
+ <label
+ for="border-bottom-right-second-radius-unit"
+ class="form__label"
+ >Unit</label
+ >
+ <select
+ name="border-bottom-right-second-radius-unit"
+ id="border-bottom-right-second-radius-unit"
+ class="form__select"
+ >
+ <option value="px" selected>px</option>
+ <option value="%">%</option>
+ <option value="em">em</option>
+ <option value="rem">rem</option>
+ </select>
+ </div>
+ </fieldset>
+ </form>
+ <div class="result">
+ <pre class="result__pre">
+ <code class="result__code"></code>
+ </pre>
+ <button id="copy-code" class="btn">Copy to the clipboard</button>
+ </div>
+ </main>
+ <footer class="footer">
+ <p class="copyright">CSS Border Previewer. MIT 2021. Armand Philippot.</p>
+ </footer>
+ <script src="app.js"></script>
+ </body>
+</html>
diff --git a/public/projects/js-small-apps/css-border-previewer/style.css b/public/projects/js-small-apps/css-border-previewer/style.css
new file mode 100644
index 0000000..458d31c
--- /dev/null
+++ b/public/projects/js-small-apps/css-border-previewer/style.css
@@ -0,0 +1,181 @@
+*,
+*::after,
+*::before {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ background: #fff;
+ color: #000;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1rem;
+ line-height: 1.618;
+ display: flex;
+ flex-flow: column nowrap;
+ min-height: 100vh;
+}
+
+.header,
+.footer,
+.result {
+ width: min(calc(100vw - 2rem), calc(1200px - 2rem));
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.preview,
+.settings {
+ width: min(calc(100vw - 2rem), calc(1200px / 2 - 2rem));
+ margin-left: auto;
+ margin-right: auto;
+}
+
+@media screen and (min-width: 1200px) {
+ .preview {
+ margin-right: 0;
+ }
+
+ .settings {
+ margin-left: 0;
+ }
+}
+
+.header {
+ padding: 2rem 0 3rem;
+ text-align: center;
+}
+
+.branding {
+ color: hsl(219, 64%, 35%);
+}
+
+.main {
+ flex: 1;
+ display: flex;
+ flex-flow: row wrap;
+ gap: 1rem;
+ margin: auto;
+}
+
+.preview {
+ border: 1px solid #ccc;
+ padding: 3rem;
+ position: relative;
+}
+
+.box {
+ border: 1px solid #000;
+ width: 100%;
+ height: 20vh;
+ position: sticky;
+ top: 3rem;
+}
+
+.result {
+ max-width: 100%;
+}
+
+.result__pre {
+ background: #333;
+ color: #fff;
+ min-height: 20rem;
+ margin: 1rem auto;
+ padding: 0 1rem;
+ overflow-x: auto;
+ tab-size: 4;
+}
+
+.result .btn {
+ margin: auto;
+}
+
+@media screen and (min-width: 1440px) {
+ .result__pre {
+ margin: 1rem 0;
+ }
+}
+
+.footer {
+ margin-top: 2rem;
+ padding: 1rem 0;
+}
+
+.copyright {
+ font-size: 0.9rem;
+ text-align: center;
+}
+
+.form p {
+ font-weight: 600;
+}
+
+.form__fieldset {
+ border-color: hsl(219, 64%, 35%);
+ margin-bottom: 1rem;
+ padding: 0.5rem 1rem 1rem;
+}
+
+.form__fieldset--flex {
+ display: flex;
+ flex-flow: row wrap;
+ gap: 1rem;
+}
+
+.form__legend {
+ color: hsl(219, 64%, 35%);
+ font-size: 0.9rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ padding: 0 0.5rem;
+}
+
+.form__item--flex {
+ display: flex;
+ flex-flow: column wrap;
+}
+
+.form__item:not(.form__item--flex) + * {
+ margin-top: 1rem;
+}
+
+.form__input,
+.form__select {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+.form__input {
+ padding: 0.2rem 0.5rem;
+}
+
+.form__select {
+ padding: 0.4rem 0.5rem;
+}
+
+.btn {
+ background: #fff;
+ border: 2px solid hsl(219, 64%, 35%);
+ box-shadow: 1px 1px 2px hsl(219, 64%, 15%), 0 0 2px 1px hsl(219, 64%, 15%);
+ color: hsl(219, 64%, 35%);
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: 600;
+ line-height: inherit;
+ display: block;
+ padding: 0.5rem;
+ cursor: pointer;
+ transition: all 0.15s ease-in-out 0s;
+}
+
+.btn:hover,
+.btn:focus {
+ background: hsl(219, 64%, 35%);
+ color: #fff;
+}
+
+.btn:active {
+ transform: translateX(1px) translateY(1px);
+}