mirror of
https://github.com/canonical/canonical-design.git
synced 2025-07-20 16:42:15 +00:00
feat: Setup up dev enviroment
This commit is contained in:

committed by
MariaPaula Trujillo

parent
9a85cf8108
commit
43df35811d
4
.env
4
.env
@ -1 +1,3 @@
|
||||
SECRET_KEY=insecure_dev_key
|
||||
SECRET_KEY=insecure_dev_key
|
||||
PORT=8052
|
||||
FLASK_DEBUG=true
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -21,9 +21,12 @@ package-lock.json
|
||||
|
||||
# [env] Local environment settings
|
||||
.*.hash
|
||||
.env.local
|
||||
|
||||
# [sass] Files generated by Sass
|
||||
*.css
|
||||
|
||||
# Project specific
|
||||
.venv/
|
||||
.dotrun.json
|
||||
|
||||
|
28
.stylelintrc
Normal file
28
.stylelintrc
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard-scss"],
|
||||
"rules": {
|
||||
"no-invalid-position-at-import-rule": null,
|
||||
"scss/at-mixin-pattern": null,
|
||||
"declaration-empty-line-before": null,
|
||||
"scss/operator-no-newline-after": null,
|
||||
"scss/dollar-variable-pattern": null,
|
||||
"scss/double-slash-comment-empty-line-before": null,
|
||||
"scss/no-global-function-names": null,
|
||||
"selector-class-pattern": null,
|
||||
"scss/at-extend-no-missing-placeholder": null,
|
||||
"at-rule-no-unknown": [ true, {
|
||||
"ignoreAtRules": [
|
||||
"extend",
|
||||
"include",
|
||||
"mixin",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"else",
|
||||
"warn",
|
||||
"return",
|
||||
"use"
|
||||
]
|
||||
} ]
|
||||
}
|
||||
}
|
10
README.md
10
README.md
@ -7,12 +7,6 @@ This is the codebase for the canonical.design static website
|
||||
This site is currently under-development.
|
||||
## Development
|
||||
|
||||
The simplest way to run the site is:
|
||||
The simplest way to run the site is with `dotrun`.
|
||||
|
||||
- Create a python virtual environment
|
||||
- install requirements
|
||||
- install node modules
|
||||
- run `yarn build`
|
||||
- run `flask run` in webapp folder
|
||||
|
||||
Afterwards the website will be available at <http://localhost:5000>.
|
||||
Afterwards the website will be available at <http://localhost:8052>.
|
||||
|
11
entrypoint
Executable file
11
entrypoint
Executable file
@ -0,0 +1,11 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
RUN_COMMAND="talisker.gunicorn webapp.app:app --bind $1 --name talisker-`hostname`"
|
||||
|
||||
if [ "${FLASK_DEBUG}" = true ] || [ "${FLASK_DEBUG}" = 1 ]; then
|
||||
RUN_COMMAND="${RUN_COMMAND} --reload --log-level debug --timeout 9999"
|
||||
fi
|
||||
|
||||
${RUN_COMMAND}
|
27
package.json
27
package.json
@ -2,8 +2,19 @@
|
||||
"name": "canonical-design",
|
||||
"description": "marketing site for design team at Canonical",
|
||||
"scripts": {
|
||||
"start": "yarn run build && concurrently --raw 'yarn run watch' 'yarn run serve'",
|
||||
"serve": "./entrypoint 0.0.0.0:${PORT}",
|
||||
"watch": "yarn run watch-css",
|
||||
"watch-css": "watch -p 'static/sass/**/*.scss' -c 'yarn run build'",
|
||||
"build": "yarn run build-css",
|
||||
"build-css": "sass static/sass/styles.scss static/css/styles.css --load-path=node_modules --style=compressed && postcss --map false --use autoprefixer --replace 'static/css/**/*.css'"
|
||||
"build-css": "sass static/sass/styles.scss static/css/styles.css --load-path=node_modules --style=compressed && postcss --map false --use autoprefixer --replace 'static/css/**/*.css'",
|
||||
"lint-python": "flake8 --extend-ignore=E203 webapp && black --check --line-length 79 webapp",
|
||||
"lint-scss": "stylelint static/**/*.scss",
|
||||
"lint-js": "prettier -c 'static/js/*.{js,jsx,ts,tsx}'",
|
||||
"format-python": "black --line-length 79 webapp",
|
||||
"format-scss": "prettier -w 'static/sass/*.scss'",
|
||||
"format-js": "prettier -w 'static/js/*.{js,jsx,ts,tsx}'",
|
||||
"format-prettier": "yarn run format-scss && yarn run format-js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -12,10 +23,18 @@
|
||||
"author": "canonical",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"sass": "1.70.0",
|
||||
"postcss": "8.4.31",
|
||||
"autoprefixer": "10.4.13",
|
||||
"global": "^4.4.0",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-cli": "10.1.0",
|
||||
"sass": "1.70.0",
|
||||
"vanilla-framework": "4.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "7.6.0",
|
||||
"prettier": "3.4.1",
|
||||
"stylelint": "16.11.0",
|
||||
"stylelint-config-standard-scss": "13.1.0",
|
||||
"watch-cli": "0.2.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,47 @@
|
||||
const initNavigationSliding = () => {
|
||||
const ANIMATION_SNAP_DURATION = 100;
|
||||
const navigation = document.querySelector('.p-navigation--sliding, .p-navigation--reduced');
|
||||
const toggles = document.querySelectorAll('.p-navigation__nav .p-navigation__link[aria-controls]:not(.js-back-button)');
|
||||
const menuButton = document.querySelector('.js-menu-button');
|
||||
const dropdownNavLists = document.querySelectorAll('.js-dropdown-nav-list');
|
||||
const topNavList = [...dropdownNavLists].filter((list) => !list.parentNode.closest('.js-dropdown-nav-list'))[0];
|
||||
const navigation = document.querySelector(
|
||||
".p-navigation--sliding, .p-navigation--reduced",
|
||||
);
|
||||
const toggles = document.querySelectorAll(
|
||||
".p-navigation__nav .p-navigation__link[aria-controls]:not(.js-back-button)",
|
||||
);
|
||||
const menuButton = document.querySelector(".js-menu-button");
|
||||
const dropdownNavLists = document.querySelectorAll(".js-dropdown-nav-list");
|
||||
const topNavList = [...dropdownNavLists].filter(
|
||||
(list) => !list.parentNode.closest(".js-dropdown-nav-list"),
|
||||
)[0];
|
||||
|
||||
const closeAllDropdowns = () => {
|
||||
resetToggles();
|
||||
navigation.classList.remove('has-menu-open');
|
||||
menuButton.innerHTML = 'Menu';
|
||||
navigation.classList.remove("has-menu-open");
|
||||
menuButton.innerHTML = "Menu";
|
||||
};
|
||||
|
||||
const keyPressHandler = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (e.key === "Escape") {
|
||||
closeAllDropdowns();
|
||||
document.removeEventListener('keydown', keyPressHandler);
|
||||
document.removeEventListener("keydown", keyPressHandler);
|
||||
}
|
||||
};
|
||||
|
||||
menuButton.addEventListener('click', function(e) {
|
||||
menuButton.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
if (navigation.classList.contains('has-menu-open')) {
|
||||
if (navigation.classList.contains("has-menu-open")) {
|
||||
closeAllDropdowns();
|
||||
} else {
|
||||
navigation.classList.add('has-menu-open');
|
||||
e.target.innerHTML = 'Close menu';
|
||||
navigation.classList.add("has-menu-open");
|
||||
e.target.innerHTML = "Close menu";
|
||||
setFocusable(topNavList);
|
||||
document.addEventListener('keydown', keyPressHandler);
|
||||
document.addEventListener("keydown", keyPressHandler);
|
||||
}
|
||||
});
|
||||
|
||||
const resetToggles = (exception) => {
|
||||
toggles.forEach(function(toggle) {
|
||||
const target = document.getElementById(toggle.getAttribute('aria-controls'));
|
||||
toggles.forEach(function (toggle) {
|
||||
const target = document.getElementById(
|
||||
toggle.getAttribute("aria-controls"),
|
||||
);
|
||||
if (!target || target === exception) {
|
||||
return;
|
||||
}
|
||||
@ -43,21 +51,29 @@ const initNavigationSliding = () => {
|
||||
|
||||
const setActiveDropdown = (dropdownToggleButton, isActive = true) => {
|
||||
// set active state of the dropdown toggle (to slide the panel into view)
|
||||
const dropdownToggleEl = dropdownToggleButton.closest('.js-navigation-dropdown-toggle');
|
||||
dropdownToggleEl?.classList.toggle('is-active', isActive);
|
||||
const dropdownToggleEl = dropdownToggleButton.closest(
|
||||
".js-navigation-dropdown-toggle",
|
||||
);
|
||||
dropdownToggleEl?.classList.toggle("is-active", isActive);
|
||||
|
||||
// set active state of the parent dropdown panel (to fade it out of view)
|
||||
const parentLevelDropdown = dropdownToggleEl.closest('.js-navigation-sliding-panel');
|
||||
parentLevelDropdown?.classList.toggle('is-active', isActive);
|
||||
const parentLevelDropdown = dropdownToggleEl.closest(
|
||||
".js-navigation-sliding-panel",
|
||||
);
|
||||
parentLevelDropdown?.classList.toggle("is-active", isActive);
|
||||
};
|
||||
|
||||
const collapseDropdown = (dropdownToggleButton, targetDropdown, animated = false) => {
|
||||
const collapseDropdown = (
|
||||
dropdownToggleButton,
|
||||
targetDropdown,
|
||||
animated = false,
|
||||
) => {
|
||||
const closeHandler = () => {
|
||||
targetDropdown.setAttribute('aria-hidden', 'true');
|
||||
targetDropdown.setAttribute("aria-hidden", "true");
|
||||
setActiveDropdown(dropdownToggleButton, false);
|
||||
};
|
||||
|
||||
targetDropdown.classList.add('is-collapsed');
|
||||
targetDropdown.classList.add("is-collapsed");
|
||||
if (animated) {
|
||||
setTimeout(closeHandler, ANIMATION_SNAP_DURATION);
|
||||
} else {
|
||||
@ -65,28 +81,36 @@ const initNavigationSliding = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const expandDropdown = (dropdownToggleButton, targetDropdown, animated = false) => {
|
||||
const expandDropdown = (
|
||||
dropdownToggleButton,
|
||||
targetDropdown,
|
||||
animated = false,
|
||||
) => {
|
||||
setActiveDropdown(dropdownToggleButton);
|
||||
targetDropdown.setAttribute('aria-hidden', 'false');
|
||||
targetDropdown.setAttribute("aria-hidden", "false");
|
||||
|
||||
if (animated) {
|
||||
// trigger the CSS transition
|
||||
requestAnimationFrame(() => {
|
||||
targetDropdown.classList.remove('is-collapsed');
|
||||
targetDropdown.classList.remove("is-collapsed");
|
||||
});
|
||||
} else {
|
||||
// make it appear immediately
|
||||
targetDropdown.classList.remove('is-collapsed');
|
||||
targetDropdown.classList.remove("is-collapsed");
|
||||
}
|
||||
|
||||
setFocusable(targetDropdown);
|
||||
};
|
||||
|
||||
// when clicking outside navigation, close all dropdowns
|
||||
document.addEventListener('click', function(event) {
|
||||
document.addEventListener("click", function (event) {
|
||||
const target = event.target;
|
||||
if (target.closest) {
|
||||
if (!target.closest('.p-navigation, .p-navigation--sliding, .p-navigation--reduced')) {
|
||||
if (
|
||||
!target.closest(
|
||||
".p-navigation, .p-navigation--sliding, .p-navigation--reduced",
|
||||
)
|
||||
) {
|
||||
closeAllDropdowns();
|
||||
}
|
||||
}
|
||||
@ -96,52 +120,60 @@ const initNavigationSliding = () => {
|
||||
// turn on focusability for all direct children in the target dropdown
|
||||
if (list) {
|
||||
for (const item of list.children) {
|
||||
item.children[0].setAttribute('tabindex', '0');
|
||||
item.children[0].setAttribute("tabindex", "0");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setFocusable = (target) => {
|
||||
// turn off focusability for all dropdown lists in the navigation
|
||||
dropdownNavLists.forEach(function(list) {
|
||||
dropdownNavLists.forEach(function (list) {
|
||||
if (list != topNavList) {
|
||||
const elements = list.querySelectorAll('ul > li > a, ul > li > button');
|
||||
elements.forEach(function(element) {
|
||||
element.setAttribute('tabindex', '-1');
|
||||
const elements = list.querySelectorAll("ul > li > a, ul > li > button");
|
||||
elements.forEach(function (element) {
|
||||
element.setAttribute("tabindex", "-1");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// if target dropdown is not a list, find the list in it
|
||||
const isList = target.classList.contains('js-dropdown-nav-list');
|
||||
const isList = target.classList.contains("js-dropdown-nav-list");
|
||||
if (!isList) {
|
||||
// find all lists in the target dropdown and make them focusable
|
||||
target.querySelectorAll('.js-dropdown-nav-list').forEach(function(element) {
|
||||
setListFocusable(element);
|
||||
});
|
||||
target
|
||||
.querySelectorAll(".js-dropdown-nav-list")
|
||||
.forEach(function (element) {
|
||||
setListFocusable(element);
|
||||
});
|
||||
} else {
|
||||
setListFocusable(target);
|
||||
}
|
||||
};
|
||||
|
||||
toggles.forEach(function(toggle) {
|
||||
toggle.addEventListener('click', function(e) {
|
||||
toggles.forEach(function (toggle) {
|
||||
toggle.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.getElementById(toggle.getAttribute('aria-controls'));
|
||||
const target = document.getElementById(
|
||||
toggle.getAttribute("aria-controls"),
|
||||
);
|
||||
if (target) {
|
||||
// check if the toggled dropdown is child of another dropdown
|
||||
const isNested = !!target.parentNode.closest('.p-navigation__dropdown');
|
||||
const isNested = !!target.parentNode.closest(".p-navigation__dropdown");
|
||||
if (!isNested) {
|
||||
resetToggles(target);
|
||||
}
|
||||
|
||||
if (target.getAttribute('aria-hidden') === 'true') {
|
||||
if (target.getAttribute("aria-hidden") === "true") {
|
||||
// only animate the dropdown if menu is not open, otherwise just switch the visible one
|
||||
expandDropdown(toggle, target, !navigation.classList.contains('has-menu-open'));
|
||||
navigation.classList.add('has-menu-open');
|
||||
expandDropdown(
|
||||
toggle,
|
||||
target,
|
||||
!navigation.classList.contains("has-menu-open"),
|
||||
);
|
||||
navigation.classList.add("has-menu-open");
|
||||
} else {
|
||||
collapseDropdown(toggle, target, true);
|
||||
navigation.classList.remove('has-menu-open');
|
||||
navigation.classList.remove("has-menu-open");
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -149,37 +181,41 @@ const initNavigationSliding = () => {
|
||||
|
||||
const goBackOneLevel = (e, backButton) => {
|
||||
e.preventDefault();
|
||||
const target = backButton.closest('.p-navigation__dropdown');
|
||||
target.setAttribute('aria-hidden', 'true');
|
||||
const target = backButton.closest(".p-navigation__dropdown");
|
||||
target.setAttribute("aria-hidden", "true");
|
||||
setActiveDropdown(backButton, false);
|
||||
setFocusable(target.parentNode.parentNode);
|
||||
};
|
||||
|
||||
dropdownNavLists.forEach(function(dropdown) {
|
||||
dropdown.children[1].addEventListener('keydown', function(e) {
|
||||
if (e.shiftKey && e.key === 'Tab' && window.getComputedStyle(dropdown.children[0], null).display === 'none') {
|
||||
dropdownNavLists.forEach(function (dropdown) {
|
||||
dropdown.children[1].addEventListener("keydown", function (e) {
|
||||
if (
|
||||
e.shiftKey &&
|
||||
e.key === "Tab" &&
|
||||
window.getComputedStyle(dropdown.children[0], null).display === "none"
|
||||
) {
|
||||
goBackOneLevel(e, dropdown.children[1].children[0]);
|
||||
dropdown.parentNode.children[0].focus({
|
||||
preventScroll: true
|
||||
preventScroll: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.js-back-button').forEach(function(backButton) {
|
||||
backButton.addEventListener('click', function(e) {
|
||||
document.querySelectorAll(".js-back-button").forEach(function (backButton) {
|
||||
backButton.addEventListener("click", function (e) {
|
||||
goBackOneLevel(e, backButton);
|
||||
});
|
||||
});
|
||||
|
||||
// throttle util (for window resize event)
|
||||
var throttle = function(fn, delay) {
|
||||
var throttle = function (fn, delay) {
|
||||
var timer = null;
|
||||
return function() {
|
||||
return function () {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function() {
|
||||
timer = setTimeout(function () {
|
||||
fn.apply(context, args);
|
||||
}, delay);
|
||||
};
|
||||
@ -188,8 +224,8 @@ const initNavigationSliding = () => {
|
||||
// hide side navigation drawer when screen is resized horizontally
|
||||
let previousWidth = window.innerWidth;
|
||||
window.addEventListener(
|
||||
'resize',
|
||||
throttle(function() {
|
||||
"resize",
|
||||
throttle(function () {
|
||||
const currentWidth = window.innerWidth;
|
||||
if (currentWidth !== previousWidth) {
|
||||
closeAllDropdowns();
|
||||
@ -199,4 +235,4 @@ const initNavigationSliding = () => {
|
||||
);
|
||||
};
|
||||
|
||||
initNavigationSliding();
|
||||
initNavigationSliding();
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Import the framework
|
||||
@import 'vanilla-framework';
|
||||
@import "vanilla-framework";
|
||||
|
||||
// Include all of Vanilla Framework
|
||||
@include vanilla;
|
||||
|
||||
|
||||
// Blockquote style override until officially in vanilla
|
||||
.p-pull-quote {
|
||||
border-left: 3px solid #e95420;
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
&:first-of-type::before,
|
||||
&:last-of-type::after {
|
||||
content: '';
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,42 +23,3 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Border for 2/3 images as per design
|
||||
.p-image-container--2-3,
|
||||
.is-underlined {
|
||||
border-bottom: 3px solid #000;
|
||||
}
|
||||
|
||||
// Brand font styling
|
||||
.font-tester__demo {
|
||||
&[data-family='monospace'] {
|
||||
font-family: "Ubuntu Mono variable";
|
||||
}
|
||||
|
||||
&[data-style='italic'] {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// Blog post needs to be cover, not contain
|
||||
.blog-p-card--post .p-image-container__image {
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Hero section styling
|
||||
#ds_hero1 {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@media (max-width: 619px) {
|
||||
#ds_hero1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,4 @@ app = FlaskBase(
|
||||
|
||||
template_finder_view = TemplateFinder.as_view("template_finder")
|
||||
app.add_url_rule("/", view_func=template_finder_view)
|
||||
app.add_url_rule("/<path:subpath>", view_func=template_finder_view)
|
||||
app.add_url_rule("/<path:subpath>", view_func=template_finder_view)
|
||||
|
Reference in New Issue
Block a user