diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..559c19d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.py]
+indent_size = 4
diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml
index 8a75849..d29271f 100644
--- a/.github/workflows/merge.yml
+++ b/.github/workflows/merge.yml
@@ -1,6 +1,8 @@
name: Merge to main
on:
+ schedule:
+ - cron: "25 6 * * *" # 6:25 am UTC every day
workflow_dispatch:
push:
branches:
@@ -31,4 +33,4 @@ jobs:
target: gh-pages
path: frontend
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 97c9ced..37bcf87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ frontend/_site/
.RData
.Ruserdata
.Rproj.user
-.idea
\ No newline at end of file
+.idea
diff --git a/doc/Contribution Flow.png b/doc/Contribution Flow.png
new file mode 100644
index 0000000..ce8d5a2
Binary files /dev/null and b/doc/Contribution Flow.png differ
diff --git a/doc/RELEASING_AN_ADVISORY.md b/doc/RELEASING_AN_ADVISORY.md
new file mode 100644
index 0000000..76b8b24
--- /dev/null
+++ b/doc/RELEASING_AN_ADVISORY.md
@@ -0,0 +1,56 @@
+# Releasing and Updating Advisories
+
+## Creating an Advisory
+
+### Process Overview
+
+1. This repository, `nr-epd-aq-statements` comprises a Quarto application that will collate and render documents in
+ Markdown format, producing a complete website with navigation and several automatically-generated lists (for example,
+ current Smoky Skies Bulletins, and historical advisories).
+2. The resulting website is published on [Github pages](https://bcgov.github.io/nr-epd-aq-statements/). It is a static
+ website, and must be rebuilt from scratch each time content changes. It must also be rebuilt to recompute lists (for
+ example, those that rely on dates, like the
+ list of active bulletins). It should ideally be rebuilt daily to remain current.
+3. To contribute a new Smoky Skies Bulletin or Advisory, use the companion Shiny application. This application will
+ produce three output files when run. Those three files should be submitted to this repository in the form of a
+ pull-request.
+4. When the pull-request is merged, the site will begin to rebuild. You can view the status of the build process
+ in [Github Actions](https://github.com/bcgov/nr-epd-aq-statements/actions). Once the action completes, the new
+ content will be available on the public [Github pages](https://bcgov.github.io/nr-epd-aq-statements/) site
+ immediately.
+
+
+
+### Pull Request Details
+
+- The pull request should be created against the `main` branch. This can be accomplished by either checking out the
+ repository locally and making changes with the `git` command line or desktop tools, or via Github's web interface
+ using the [upload tool](https://github.com/bcgov/nr-epd-aq-statements/upload/main).
+- Regardless of the method for creating the pull request, it should include three files:
+ - `map.html`
+ - dated markdown file (eg `2025-02-06_Smoky-Skies_Issue.md`)
+ - corresponding PDF file (eg `2025-02-06_Smoky-Skies_Issue.pdf`)
+- The files should be contributed to the project in the directory `frontend/statements/YYYY-MM/DD`. Separating the files
+ in this way ensures that `map.html` is not overwritten (since each statement has a corresponding map file and its name
+ is not easily changed).
+- If more than one statement is created on a given day, consider creating them in further subfolders with a meaninful
+ name, such as the region or the hour of issue.
+
+## Updating or Removing an Advisory
+
+### Updating an existing Advisory
+
+To update an existing advisory, follow the same steps as above to regenerate the advisory and create a pull request that
+**overwrites** the files you wish to edit. After the pull request is merged and the site rebuilt, the content will be
+updated.
+
+You may also create a pull request manually with the Github website if you wish to manually update specific wording
+within a statement, advisory, or bulletin. Be aware that you can only edit the generated `html` content this way, and
+that corresponding `pdf`s are passed through unmodified (so if you need to change a PDF too, you must make the change by
+editing the template in the Shiny app before exporting it),
+
+### Removing an Advisory
+
+To remove an advisory, create a pull request against the main branch that deletes the three files (markdown, pdf,
+and `map.html`). When the site is rebuilt, the content will be removed and will not be accessible via any listings or
+even directly by the URL.
diff --git a/doc/UPDATING_STATIC_CONTENT.md b/doc/UPDATING_STATIC_CONTENT.md
new file mode 100644
index 0000000..23c865f
--- /dev/null
+++ b/doc/UPDATING_STATIC_CONTENT.md
@@ -0,0 +1,23 @@
+# Updating Static Content
+
+## Content
+
+There are four pages that provide base content for the static site. They are:
+
+- `index.qmd` - the home page
+- `measuring.qmd` - 'Measuring air quality' document
+- `shortcode_demos.qmd` (provided as an example)
+- `statements.qmd` - A dynamic list of historic statements. Contents are generated automatically based on the contents
+ of the preamble ([Quarto Custom Listings](https://quarto.org/docs/websites/website-listings-custom.html))
+
+These four documents are written in QMD format and can be edited via Github Pull Request against the `main` branch.
+
+You can add or remove content as required to edit the static content of the site. Create, edit, or delete files with
+a `.qmd` extension in the `frontend` directory. Any file whose name starts with an underscore `_` will be ignored by
+Quarto during the rendering step.
+
+## Navigation
+
+You may wish to add, remove, or reorder the contents of the site navigation top bar or sidebar. They will not be updated
+automatically if you add or delete static content files. These elements are defined in the
+file `_extensions/bcds/_extension.yaml` under the key `contributes.project.website`.
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 0000000..ddfa4f0
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,9 @@
+/.quarto/
+
+/env/
+
+_recent_statements.yaml
+_ssb.yaml
+
+# for local testing
+Caddyfile
diff --git a/frontend/Caddyfile b/frontend/Caddyfile
new file mode 100644
index 0000000..8af0e41
--- /dev/null
+++ b/frontend/Caddyfile
@@ -0,0 +1,23 @@
+{
+ auto_https off
+}
+:3000
+
+root * _site
+file_server
+
+log {
+ level WARN
+}
+encode gzip
+
+route {
+ header /* Cache-Control max-age=3
+}
+
+route {
+ try_files {path} /index.html
+ header /index.html {
+ Cache-Control max-age=0,no-cache,no-store,must-revalidate
+ }
+}
diff --git a/frontend/_aqs_listing.ejs b/frontend/_aqs_listing.ejs
new file mode 100644
index 0000000..759e1d0
--- /dev/null
+++ b/frontend/_aqs_listing.ejs
@@ -0,0 +1,26 @@
+```{=html}
+<% if (items.length > 0) { %>
+
"
+ end
+
+ if (useIcons == "true") then
+ if (variant ~= nil and variant ~= "") then
+ selected_icon = icon_variant_map[variant]
+ if selected_icon ~= nil then
+ markup = markup .. ''
+ end
+ end
+ end
+
+ markup = markup .. "
"
+
+
+ return pandoc.RawInline("html", markup)
+ else
+ return pandoc.Null()
+ end
+ end,
+
+ ["card_end"] = function(args, kwargs, meta)
+ if quarto.doc.is_format("html:js") then
+ local markup = "
"
+ return pandoc.RawInline("html", markup)
+ else
+ return pandoc.Null()
+ end
+ end,
+
+ ["inline_alert_start"] = function(args, kwargs, meta)
+ local title = pandoc.utils.stringify(kwargs["title"])
+ local variant = pandoc.utils.stringify(kwargs["variant"])
+
+
+ if (variant == "") then
+ variant = "info"
+ end
+
+ local icon_variant_map = {
+ ['success'] = 'check-circle',
+ ['info'] = 'info-circle',
+ ['warning'] = 'exclamation-triangle',
+ ['danger'] = 'exclamation-octagon'
+ }
+
+ local selected_icon = nil
+
+
+ if quarto.doc.is_format("html:js") then
+ local markup = "
"
+ if (variant ~= nil) then
+ selected_icon = icon_variant_map[variant]
+ if selected_icon ~= nil then
+ markup = markup .. ''
+ end
+ end
+
+ markup = markup .. "
"
+
+ if (title ~= nil) then
+ markup = markup .. ""
+ markup = markup .. title
+ markup = markup .. ""
+ end
+
+ return pandoc.RawInline("html", markup)
+ else
+ return pandoc.Null()
+ end
+ end,
+
+ ["inline_alert_end"] = function(args, kwargs, meta)
+ if quarto.doc.is_format("html:js") then
+ local markup = "
"
+ return pandoc.RawInline("html", markup)
+ else
+ return pandoc.Null()
+ end
+ end,
+}
diff --git a/frontend/_extensions/bcds/bcds.scss b/frontend/_extensions/bcds/bcds.scss
new file mode 100644
index 0000000..fb7b6b6
--- /dev/null
+++ b/frontend/_extensions/bcds/bcds.scss
@@ -0,0 +1,226 @@
+/* From BC Design System Design Tokens */
+
+/*-- scss:defaults --*/
+
+// this isn't really necessary since Quarto includes it, but it does improve code completion in IDEs
+@import "variables.scss";
+
+
+$primary: $surface-color-primary-default;
+$secondary: $surface-color-secondary-default;
+$light: $surface-color-background-light-gray;
+
+$min-contrast-ratio: 2.6 !default;
+
+$enable-rounded: false;
+$enable-transitions: false;
+
+$max-width: 1100px;
+
+$font-size: $typography-font-size-body;
+$fontcolor: $typography-color-primary;
+$linkcolor: $typography-color-link;
+$linestretch: $typography-line-heights-regular;
+$backgroundcolor: $surface-color-background-white;
+
+$grid-body-width: 1100px;
+
+$font-family-sans-serif: $typography-font-families-bc-sans, sans-serif;
+$headings-font-weight: $typography-font-weights-bold;
+
+$navbar-bg: $surface-color-background-white;
+$navbar-fg: $typography-color-link;
+
+$progress-height: .5rem !default;
+
+
+$bs-card-spacer-y: 0;
+$bs-card-spacer-x: 0;
+
+body {
+ -webkit-font-smoothing: antialiased;
+}
+
+
+@font-face {
+ font-family: BC Sans;
+ font-style: normal;
+ font-weight: 400;
+ src: url(/_extensions/bcds/assets/static/BCSans-Regular.DKwZ9GnR.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-Regular.CMx_o1HH.woff) format("woff")
+}
+
+@font-face {
+ font-family: BC Sans;
+ font-style: italic;
+ font-weight: 400;
+ src: url(/_extensions/bcds/assets/static/BCSans-Italic._P9wZbgV.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-Italic.BSFPKGgR.woff) format("woff")
+}
+
+@font-face {
+ font-family: BC Sans;
+ font-style: normal;
+ font-weight: 700;
+ src: url(/_extensions/bcds/assets/static/BCSans-Bold.Ciclm6eX.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-Bold.BmoTexmK.woff) format("woff")
+}
+
+@font-face {
+ font-family: BC Sans;
+ font-style: italic;
+ font-weight: 700;
+ src: url(/_extensions/bcds/assets/static/BCSans-BoldItalic.bEalI5bL.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-BoldItalic.Dg_86UDa.woff) format("woff")
+}
+
+@font-face {
+ font-family: BC Sans;
+ font-style: normal;
+ font-weight: 300;
+ src: url(/_extensions/bcds/assets/static/BCSans-Light.DTetth3X.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-Light.CmVrKSZi.woff) format("woff")
+}
+
+@font-face {
+ font-family: BC Sans;
+ font-style: italic;
+ font-weight: 300;
+ src: url(/_extensions/bcds/assets/static/BCSans-LightItalic.dN5bWDr3.woff2) format("woff2"), url(/_extensions/bcds/assets/static/BCSans-LightItalic.C2AxY9aU.woff) format("woff")
+}
+
+/*-- scss:rules --*/
+h1, h2, h3, h4, h5, h6 {
+ border: none;
+ line-height: $typography-line-heights-sparse;
+
+ &:before {
+ content: ' ';
+ width: 40px;
+ display: block;
+ border-bottom: $layout-padding-small solid $theme-primary-gold;
+ padding: 0;
+ margin: 0 0 $layout-padding-small;
+ //margin: 0;
+ }
+
+}
+
+.quarto-title {
+ display: none;
+}
+
+hr {
+ color: $theme-primary-gold;
+ height: $layout-border-width-large;
+ width: 3rem;
+}
+
+
+#quarto-header {
+ .navbar {
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 0;
+ padding-right: 0;
+
+ width: 100%;
+
+ .navbar-brand-container {
+
+ .navbar-logo {
+ max-height: unset;
+ padding-right: $layout-padding-medium;
+ padding-left: 0;
+ }
+
+ .navbar-brand-logo {
+ padding: 0;
+
+ @media only screen and (max-width: 575px) {
+
+ /* Replace the link with a block element having a background, to work round Quarto's single logo spec */
+ border: none !important; /* override bootstrap default */
+ background-image: url(/_extensions/bcds/assets/BCID_V_rgb_pos.png);
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ width: 48px;
+ min-height: 48px;
+ display: block;
+
+ img.navbar-logo {
+ display: none;
+ }
+ }
+ @media only screen and (max-width: 991px) {
+ height: 60px;
+ border-right: 1px solid $theme-gray-50;
+ }
+ @media only screen and (min-width: 992px) {
+ height: 80px;
+ border-right: 1px solid $theme-gray-50;
+ }
+
+ margin: auto 0;
+ }
+
+ .navbar-title {
+ padding-left: $layout-padding-medium;
+ font-weight: $typography-font-weights-regular;
+ text-transform: capitalize;
+ }
+ }
+
+ }
+}
+
+/* The BCDS footer component is pre-styled. Resetting the default Quarto CSS to give it space to apply its styles */
+.nav-footer {
+ display: block;
+ padding-left: 0 !important;
+ padding-right: 0 !important;
+
+ .nav-footer-left {
+ display: none;
+ }
+
+ .nav-footer-right {
+ display: none;
+ }
+
+ .nav-footer-center {
+ text-align: initial;
+ }
+}
+
+/* Styling sidebar to match Figma specs */
+
+#quarto-sidebar {
+
+ .sidebar-item {
+ padding: 0;
+ margin: 0;
+ }
+
+ .sidebar-link {
+ padding: $layout-padding-small;
+ }
+
+ .sidebar-item-container .sidebar-link.active {
+ background: $surface-color-background-light-blue;
+ border-left: $layout-border-width-large solid $theme-blue-70;
+ font-weight: $typography-font-weights-bold;
+ }
+}
+
+/*
+ Allow these elements to scroll horizontally on smaller screens
+ */
+
+.quarto-listing {
+ @media only screen and (max-width: 575px) {
+ overflow-x: scroll;
+ }
+}
+
+table {
+ @media only screen and (max-width: 575px) {
+ overflow-x: scroll;
+ }
+}
diff --git a/frontend/_extensions/bcds/variables.scss b/frontend/_extensions/bcds/variables.scss
new file mode 100644
index 0000000..442e518
--- /dev/null
+++ b/frontend/_extensions/bcds/variables.scss
@@ -0,0 +1,509 @@
+/*-- scss:defaults --*/
+
+/**
+ * Do not edit directly, this file was auto-generated.
+ */
+
+$surface-opacity-0: 0;
+$surface-opacity-10: 0.1;
+$surface-opacity-20: 0.2;
+$surface-opacity-30: 0.3;
+$surface-opacity-40: 0.4;
+$surface-opacity-50: 0.5;
+$surface-opacity-60: 0.6;
+$surface-opacity-70: 0.7;
+$surface-opacity-80: 0.8;
+$surface-opacity-90: 0.9;
+$surface-opacity-100: 1;
+$surface-shadow-none: 0 0.30000001192092896px 0.8999999761581421px 0 #0000001a, 0 1.600000023841858px 3.5999999046325684px 0 #00000021; // Default shadow for most content and UI elements
+$surface-shadow-small: 0 0.6000000238418579px 1.7999999523162842px 0 #0000001a, 0 3.200000047683716px 7.199999809265137px 0 #00000021; // Reduced shadow for secondary UI elements
+$surface-shadow-medium: 0 1.2000000476837158px 3.5999999046325684px 0 #0000001a, 0 6.400000095367432px 14.399999618530273px 0 #00000021; // Standard shadow for UI elements like cards and buttons
+$surface-shadow-large: 0 4.800000190734863px 14.399999618530273px 0 #0000002e, 0 25.600000381469727px 57.599998474121094px 0 #00000038; // Larger shadow for emphasis. Use sparingly
+$surface-color-primary-button-default: #013366; // Default fill colour for primary buttons
+$surface-color-primary-button-hover: #1E5189; // Fill colour used when user hovers over primary button
+$surface-color-primary-button-disabled: #EDEBE9; // Fill colour for inactive primary buttons
+$surface-color-primary-danger-button-default: #CE3E39; // Default fill colour for buttons in danger/warning state
+$surface-color-primary-danger-button-hover: #A2312D; // Fill colour used when user hovers over danger/warning button
+$surface-color-primary-danger-button-disabled: #EDEBE9; // Default fill colour for disabled danger/warning button
+$surface-color-primary-default: #013366; // Default theme colour for components and layout
+$surface-color-primary-hover: #1E5189; // Hover variant of surface.color.primary.default
+$surface-color-primary-pressed: #01264C; // Selected/pressed variant of surface.color.primary.default
+$surface-color-primary-disabled: #EDEBE9; // Disabled/inactive variant of surface.color.primary default
+$surface-color-secondary-button-default: #FFFFFF; // Fill colour for secondary buttons
+$surface-color-secondary-button-hover: #EDEBE9; // Fill colour used when user hovers over secondary button
+$surface-color-secondary-button-disabled: #EDEBE9; // Fill colour for inactive secondary buttons
+$surface-color-secondary-default: #FFFFFF; // Light theme colour for components and layout
+$surface-color-secondary-hover: #EDEBE9; // Hover variant of surface.color.secondary.default
+$surface-color-secondary-pressed: #E0DEDC; // Selected/pressed variant of surface.color.secondary.default
+$surface-color-secondary-disabled: #EDEBE9; // Disabled/inactive variant of surface.color.secondary default
+$surface-color-tertiary-button-default: rgba(255,255,255,0); // Default fill colour for tertiary buttons
+$surface-color-tertiary-button-hover: #ECEAE8; // Fill colour used when user hovers over tertiary button
+$surface-color-tertiary-button-disabled: #EDEBE9; // Fill colour for inactive tertiary buttons
+$surface-color-tertiary-default: #FFFFFF00; // Alternative light theme colour for components and layout
+$surface-color-tertiary-hover: #ECEAE8; // Hover variant of surface.color.tertiary.default
+$surface-color-tertiary-pressed: #E0DEDC; // Selected/pressed variant of surface.color.tertiary.default
+$surface-color-tertiary-disabled: #EDEBE9; // Disabled/inactive variant of surface.color.tertiary default
+$surface-color-menus-default: #FFFFFF; // Default fill colour for menu items
+$surface-color-menus-hover: #EDEBE9; // Fill colour used when user hovers over a menu item
+$surface-color-forms-default: #FFFFFF; // Default fill colour for form/input fields
+$surface-color-forms-disabled: #EDEBE9; // Fill colour for inactive/disabled form/input fields
+$surface-color-background-white: #FFFFFF; // White background
+$surface-color-background-light-gray: #FAF9F8; // Default background colour for layout
+$surface-color-background-light-blue: #F1F8FE; // Light blue background
+$surface-color-background-dark-blue: #053662; // Dark blue background
+$surface-color-border-default: #D8D8D8; // Lighter border colour for UI elements
+$surface-color-border-medium: #898785; // Default border colour for UI elements
+$surface-color-border-dark: #353433; // Used as a hover colour for form fields and inputs
+$surface-color-border-active: #2E5DD7; // Used as the active stroke colour for UI elements
+$surface-color-overlay-default: rgba(0,0,0,0.45); // Background overlay colour for images and modal dialogues
+$support-surface-color-info: #F7F9FC;
+$support-surface-color-danger: #F4E1E2; // Background colour for error, failure or danger messages
+$support-surface-color-success: #F6FFF8; // Background colour for success messages
+$support-surface-color-warning: #FEF1D8; // Background colour for warning messages
+$support-border-color-info: #053662; // Border stroke for general/informational messages
+$support-border-color-danger: #CE3E39; // Border stroke for error, failure or danger messages
+$support-border-color-success: #42814A; // Border stroke for success messages
+$support-border-color-warning: #F8BB47; // Border stroke for warning messages
+$icons-size-xsmall: 14px; // Smallest icon size
+$icons-size-small: 16px;
+$icons-size-medium: 20px; // Default icon size
+$icons-size-large: 24px;
+$icons-size-xlarge: 32px; // Largest icon size
+$icons-color-primary: #2D2D2D; // Default colour for icons
+$icons-color-secondary: #474543; // Alternative colour for secondary/miscellaneous icons
+$icons-color-disabled: #9F9D9C; // Use only for inactive/disabled icons
+$icons-color-link: #255A90; // Use for hyperlinked icons
+$icons-color-info: #053662; // Emphasis colour for icons in general/informational state
+$icons-color-danger: #CE3E39; // Emphasis colour for icons in error, failure or danger state
+$icons-color-success: #42814A; // Emphasis colour for icons in success state
+$icons-color-warning: #F8BB47; // Emphasis colour for icons in warning state
+$icons-color-primary-invert: #FFFFFF; // Default colour for icons on dark backgrounds
+$layout-border-width-none: 0;
+$layout-border-width-small: 1px;
+$layout-border-width-medium: 2px;
+$layout-border-width-large: 4px;
+$layout-padding-none: 0; // No padding
+$layout-padding-hair: 0.125rem; // Smallest padding value for layout
+$layout-padding-xsmall: 0.25rem;
+$layout-padding-small: 0.5rem;
+$layout-padding-medium: 1rem; // Default padding value for layout
+$layout-padding-large: 1.5rem;
+$layout-padding-xlarge: 2rem; // Largest padding value for layout
+$layout-margin-none: 0rem; // No margin
+$layout-margin-hair: 0.125rem; // Smallest margin value for layout
+$layout-margin-xsmall: 0.25rem;
+$layout-margin-small: 0.5rem; // Default margin value for layout
+$layout-margin-medium: 1rem;
+$layout-margin-large: 1.5rem;
+$layout-margin-xlarge: 2rem;
+$layout-margin-xxlarge: 2.5rem;
+$layout-margin-xxxlarge: 3rem;
+$layout-margin-huge: 3.5rem; // Largest margin value for layout
+$layout-border-radius-none: 0;
+$layout-border-radius-small: 2px;
+$layout-border-radius-medium: 4px;
+$layout-border-radius-large: 6px;
+$layout-border-radius-circular: 9999px; // Used for components with a circular radius
+$typography-font-families-bc-sans: 'BC Sans'; // BC Sans font. Requires BC Sans package
+$typography-line-heights-xxxdense: 1.125rem; // Smallest line height
+$typography-line-heights-xxdense: 1.313rem;
+$typography-line-heights-xdense: 1.688rem;
+$typography-line-heights-dense: 1.913rem;
+$typography-line-heights-regular: 2.125rem; // Default line height
+$typography-line-heights-sparse: 2.25rem;
+$typography-line-heights-xsparse: 3rem;
+$typography-line-heights-xxsparse: 3.375rem; // Largest line height
+$typography-line-heights-auto: AUTO; // System token, used to generate type styles
+$typography-font-weights-regular: 400; // System token, used to generate text styles
+$typography-font-weights-bold: 700; // System token, used to generate text styles
+$typography-font-weights-italic-weight: 400; // System token, used to generate text styles
+$typography-font-weights-italic-style: italic; // System token, used to generate text styles
+$typography-font-size-label: 0.75rem;
+$typography-font-size-small-body: 0.875rem;
+$typography-font-size-body: 1rem;
+$typography-font-size-large-body: 1.125rem;
+$typography-font-size-h5: 1.25rem;
+$typography-font-size-h4: 1.5rem;
+$typography-font-size-h3: 1.75rem;
+$typography-font-size-h2: 2rem;
+$typography-font-size-h1: 2.25rem;
+$typography-font-size-display: 3rem;
+$typography-letter-spacing-0: 0%; // System token, used to generate type styles
+$typography-paragraph-spacing-0: 0; // System token, used to generate type styles
+$typography-regular-label: 400 0.75rem/1.125rem 'BC Sans'; // Labels and captions
+$typography-regular-small-body: 400 0.875rem/1.313rem 'BC Sans'; // Smaller body text
+$typography-regular-body: 400 1rem/1.688rem 'BC Sans'; // Default body text
+$typography-regular-large-body: 400 1.125rem/1.913rem 'BC Sans'; // Larger body text
+$typography-regular-h6: 400 1.125rem/1.913rem 'BC Sans'; // Alternate (unbolded) style for Heading 6
+$typography-regular-h5: 400 1.25rem/2.125rem 'BC Sans'; // Alternate (unbolded) style for Heading 5
+$typography-regular-h4: 400 1.5rem/2.25rem 'BC Sans'; // Alternate (unbolded) style for Heading 4
+$typography-regular-h3: 400 1.75rem/3rem 'BC Sans'; // Alternate (unbolded) style for Heading 3
+$typography-regular-h2: 400 2rem/3.375rem 'BC Sans'; // Alternate (unbolded) style for Heading 2
+$typography-regular-h1: 400 2.25rem/3rem 'BC Sans'; // Alternate (unbolded) style for Heading 1
+$typography-regular-display: 400 3rem/AUTO 'BC Sans'; // Extra-large body text size. Do not use for headings
+$typography-bold-label: 700 0.75rem/1.125rem 'BC Sans'; // Labels and captions (bolded)
+$typography-bold-small-body: 700 0.875rem/1.313rem 'BC Sans'; // Default for small body text (bolded)
+$typography-bold-body: 700 1rem/1.688rem 'BC Sans'; // Default for body text (bolded)
+$typography-bold-large-body: 700 1.125rem/1.913rem 'BC Sans'; // Larger text size for body text (bolded)
+$typography-bold-h6: 700 1.125rem/1.913rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H6
+$typography-bold-h5: 700 1.25rem/2.125rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H5
+$typography-bold-h4: 700 1.5rem/2.25rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H4
+$typography-bold-h3: 700 1.75rem/3rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H3
+$typography-bold-h2: 700 2rem/3rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H2
+$typography-bold-h1: 700 2.25rem/3.375rem 'BC Sans'; // Default style for Heading 6. Includes all sizing/spacing values for H1
+$typography-bold-display: 700 3rem/AUTO 'BC Sans'; // Extra-large font size (bolded). Do not use for headings
+$typography-italic-label: 400 italic 0.75rem/1.125rem 'BC Sans'; // Labels and captions (italic)
+$typography-italic-small-body: 400 italic 0.875rem/1.313rem 'BC Sans'; // Small body text (italic)
+$typography-italic-body: 400 italic 1rem/1.688rem 'BC Sans'; // Default for body text (italic)
+$typography-italic-large-body: 400 italic 1.125rem/1.913rem 'BC Sans'; // Larger text size for body text (italic)
+$typography-italic-h6: 400 italic 1.125rem/1.913rem 'BC Sans'; // Alternate (italic) style for Heading 6
+$typography-italic-h5: 400 italic 1.25rem/2.125rem 'BC Sans'; // Alternate (italic) style for Heading 5
+$typography-italic-h4: 400 italic 1.5rem/2.25rem 'BC Sans'; // Alternate (italic) style for Heading 4
+$typography-italic-h3: 400 italic 1.75rem/3.375rem 'BC Sans'; // Alternate (italic) style for Heading 3
+$typography-italic-h2: 400 italic 2rem/3rem 'BC Sans'; // Alternate (italic) style for Heading 2
+$typography-italic-h1: 400 italic 2.25rem/3.375rem 'BC Sans'; // Alternate (italic) style for Heading 1
+$typography-italic-display: 400 italic 3rem/AUTO 'BC Sans'; // Extra-large font size (italic). Do not use for headings
+$typography-text-case-none: none;
+$typography-text-decoration-none: none; // System token, used to generate type styles
+$typography-paragraph-indent-0: 0px; // System token, used to generate type styles
+$typography-color-primary: #2D2D2D; // Default colour for all body text and headings
+$typography-color-secondary: #474543; // Alternative colour for secondary/miscellaneous text
+$typography-color-placeholder: #9F9D9C; // Use for placeholder text in forms
+$typography-color-disabled: #9F9D9C; // Use only for text in inactive user interface elements
+$typography-color-link: #255A90; // Use for hyperlinks in body text and headings
+$typography-color-danger: #CE3E39; // Use for error, failure or danger message text
+$typography-color-primary-invert: #FFFFFF; // Default colour for text on dark backgrounds
+$typography-color-secondary-invert: #ECEAE8; // Secondary colour for text on dark backgrounds
+$theme-gold-10: #FEF8E8; // Gold scale — lightest
+$theme-gold-20: #FEF0D8; // Gold scale
+$theme-gold-30: #FDE9C4; // Gold scale
+$theme-gold-40: #FCE2B0; // Gold scale
+$theme-gold-50: #FBDA9D; // Gold scale
+$theme-gold-60: #FBD389; // Gold scale
+$theme-gold-70: #FACC75; // Gold scale
+$theme-gold-80: #F9C462; // Gold scale
+$theme-gold-90: #F8BA47; // Gold scale
+$theme-gold-100: #FCBA19; // Gold scale — darkest
+$theme-blue-10: #F1F8FE; // Blue scale — lightest
+$theme-blue-20: #D8EAFD; // Blue scale
+$theme-blue-30: #C1DDFC; // Blue scale
+$theme-blue-40: #A8D0FB; // Blue scale
+$theme-blue-50: #91C4FA; // Blue scale
+$theme-blue-60: #7AB8F9; // Blue scale
+$theme-blue-70: #5595D9; // Blue scale
+$theme-blue-80: #3470B1; // Blue scale
+$theme-blue-90: #1E5189; // Blue scale
+$theme-blue-100: #013366; // Blue scale — darkest
+$theme-gray-10: #FAF9F8; // Greyscale — lightest
+$theme-gray-20: #F3F2F1; // Greyscale
+$theme-gray-30: #ECEAE8; // Greyscale
+$theme-gray-40: #E0DEDC; // Greyscale
+$theme-gray-50: #D1CFCD; // Greyscale
+$theme-gray-60: #C6C5C3; // Greyscale
+$theme-gray-70: #9F9D9C; // Greyscale
+$theme-gray-80: #605E5C; // Greyscale
+$theme-gray-90: #3D3C3B; // Greyscale
+$theme-gray-100: #353433; // Greyscale
+$theme-gray-110: #252423; // Greyscale — darkest
+$theme-gray-white: #FFFFFF; // White
+$theme-primary-blue: #013366; // Primary Blue (Blue 100)
+$theme-primary-gold: #FCBA19; // Primary Gold (Gold 100)
+
+$bcds: (
+ 'surface': (
+ 'opacity': (
+ '0': $surface-opacity-0,
+ '10': $surface-opacity-10,
+ '20': $surface-opacity-20,
+ '30': $surface-opacity-30,
+ '40': $surface-opacity-40,
+ '50': $surface-opacity-50,
+ '60': $surface-opacity-60,
+ '70': $surface-opacity-70,
+ '80': $surface-opacity-80,
+ '90': $surface-opacity-90,
+ '100': $surface-opacity-100
+ ),
+ 'shadow': (
+ 'none': $surface-shadow-none,
+ 'small': $surface-shadow-small,
+ 'medium': $surface-shadow-medium,
+ 'large': $surface-shadow-large
+ ),
+ 'color': (
+ 'primary': (
+ 'button': (
+ 'default': $surface-color-primary-button-default,
+ 'hover': $surface-color-primary-button-hover,
+ 'disabled': $surface-color-primary-button-disabled
+ ),
+ 'dangerButton': (
+ 'default': $surface-color-primary-danger-button-default,
+ 'hover': $surface-color-primary-danger-button-hover,
+ 'disabled': $surface-color-primary-danger-button-disabled
+ ),
+ 'default': $surface-color-primary-default,
+ 'hover': $surface-color-primary-hover,
+ 'pressed': $surface-color-primary-pressed,
+ 'disabled': $surface-color-primary-disabled
+ ),
+ 'secondary': (
+ 'button': (
+ 'default': $surface-color-secondary-button-default,
+ 'hover': $surface-color-secondary-button-hover,
+ 'disabled': $surface-color-secondary-button-disabled
+ ),
+ 'default': $surface-color-secondary-default,
+ 'hover': $surface-color-secondary-hover,
+ 'pressed': $surface-color-secondary-pressed,
+ 'disabled': $surface-color-secondary-disabled
+ ),
+ 'tertiary': (
+ 'button': (
+ 'default': $surface-color-tertiary-button-default,
+ 'hover': $surface-color-tertiary-button-hover,
+ 'disabled': $surface-color-tertiary-button-disabled
+ ),
+ 'default': $surface-color-tertiary-default,
+ 'hover': $surface-color-tertiary-hover,
+ 'pressed': $surface-color-tertiary-pressed,
+ 'disabled': $surface-color-tertiary-disabled
+ ),
+ 'menus': (
+ 'default': $surface-color-menus-default,
+ 'hover': $surface-color-menus-hover
+ ),
+ 'forms': (
+ 'default': $surface-color-forms-default,
+ 'disabled': $surface-color-forms-disabled
+ ),
+ 'background': (
+ 'white': $surface-color-background-white,
+ 'lightGray': $surface-color-background-light-gray,
+ 'lightBlue': $surface-color-background-light-blue,
+ 'darkBlue': $surface-color-background-dark-blue
+ ),
+ 'border': (
+ 'default': $surface-color-border-default,
+ 'medium': $surface-color-border-medium,
+ 'dark': $surface-color-border-dark,
+ 'active': $surface-color-border-active
+ ),
+ 'overlay': (
+ 'default': $surface-color-overlay-default
+ )
+ )
+ ),
+ 'support': (
+ 'surfaceColor': (
+ 'info': $support-surface-color-info,
+ 'danger': $support-surface-color-danger,
+ 'success': $support-surface-color-success,
+ 'warning': $support-surface-color-warning
+ ),
+ 'borderColor': (
+ 'info': $support-border-color-info,
+ 'danger': $support-border-color-danger,
+ 'success': $support-border-color-success,
+ 'warning': $support-border-color-warning
+ )
+ ),
+ 'icons': (
+ 'size': (
+ 'xsmall': $icons-size-xsmall,
+ 'small': $icons-size-small,
+ 'medium': $icons-size-medium,
+ 'large': $icons-size-large,
+ 'xlarge': $icons-size-xlarge
+ ),
+ 'color': (
+ 'primary': $icons-color-primary,
+ 'secondary': $icons-color-secondary,
+ 'disabled': $icons-color-disabled,
+ 'link': $icons-color-link,
+ 'info': $icons-color-info,
+ 'danger': $icons-color-danger,
+ 'success': $icons-color-success,
+ 'warning': $icons-color-warning,
+ 'primaryInvert': $icons-color-primary-invert
+ )
+ ),
+ 'layout': (
+ 'borderWidth': (
+ 'none': $layout-border-width-none,
+ 'small': $layout-border-width-small,
+ 'medium': $layout-border-width-medium,
+ 'large': $layout-border-width-large
+ ),
+ 'padding': (
+ 'none': $layout-padding-none,
+ 'hair': $layout-padding-hair,
+ 'xsmall': $layout-padding-xsmall,
+ 'small': $layout-padding-small,
+ 'medium': $layout-padding-medium,
+ 'large': $layout-padding-large,
+ 'xlarge': $layout-padding-xlarge
+ ),
+ 'margin': (
+ 'none': $layout-margin-none,
+ 'hair': $layout-margin-hair,
+ 'xsmall': $layout-margin-xsmall,
+ 'small': $layout-margin-small,
+ 'medium': $layout-margin-medium,
+ 'large': $layout-margin-large,
+ 'xlarge': $layout-margin-xlarge,
+ 'xxlarge': $layout-margin-xxlarge,
+ 'xxxlarge': $layout-margin-xxxlarge,
+ 'huge': $layout-margin-huge
+ ),
+ 'borderRadius': (
+ 'none': $layout-border-radius-none,
+ 'small': $layout-border-radius-small,
+ 'medium': $layout-border-radius-medium,
+ 'large': $layout-border-radius-large,
+ 'circular': $layout-border-radius-circular
+ )
+ ),
+ 'typography': (
+ 'fontFamilies': (
+ 'bc-sans': $typography-font-families-bc-sans
+ ),
+ 'lineHeights': (
+ 'xxxdense': $typography-line-heights-xxxdense,
+ 'xxdense': $typography-line-heights-xxdense,
+ 'xdense': $typography-line-heights-xdense,
+ 'dense': $typography-line-heights-dense,
+ 'regular': $typography-line-heights-regular,
+ 'sparse': $typography-line-heights-sparse,
+ 'xsparse': $typography-line-heights-xsparse,
+ 'xxsparse': $typography-line-heights-xxsparse,
+ 'auto': $typography-line-heights-auto
+ ),
+ 'fontWeights': (
+ 'regular': $typography-font-weights-regular,
+ 'bold': $typography-font-weights-bold,
+ 'italic': (
+ 'weight': $typography-font-weights-italic-weight,
+ 'style': $typography-font-weights-italic-style
+ )
+ ),
+ 'fontSize': (
+ 'label': $typography-font-size-label,
+ 'smallBody': $typography-font-size-small-body,
+ 'body': $typography-font-size-body,
+ 'largeBody': $typography-font-size-large-body,
+ 'H5': $typography-font-size-h5,
+ 'H4': $typography-font-size-h4,
+ 'H3': $typography-font-size-h3,
+ 'H2': $typography-font-size-h2,
+ 'H1': $typography-font-size-h1,
+ 'display': $typography-font-size-display
+ ),
+ 'letterSpacing': (
+ '0': $typography-letter-spacing-0
+ ),
+ 'paragraphSpacing': (
+ '0': $typography-paragraph-spacing-0
+ ),
+ 'regular': (
+ 'label': $typography-regular-label,
+ 'smallBody': $typography-regular-small-body,
+ 'body': $typography-regular-body,
+ 'largeBody': $typography-regular-large-body,
+ 'H6': $typography-regular-h6,
+ 'H5': $typography-regular-h5,
+ 'H4': $typography-regular-h4,
+ 'H3': $typography-regular-h3,
+ 'H2': $typography-regular-h2,
+ 'H1': $typography-regular-h1,
+ 'display': $typography-regular-display
+ ),
+ 'bold': (
+ 'label': $typography-bold-label,
+ 'smallBody': $typography-bold-small-body,
+ 'body': $typography-bold-body,
+ 'largeBody': $typography-bold-large-body,
+ 'H6': $typography-bold-h6,
+ 'H5': $typography-bold-h5,
+ 'H4': $typography-bold-h4,
+ 'H3': $typography-bold-h3,
+ 'H2': $typography-bold-h2,
+ 'H1': $typography-bold-h1,
+ 'display': $typography-bold-display
+ ),
+ 'italic': (
+ 'label': $typography-italic-label,
+ 'smallBody': $typography-italic-small-body,
+ 'body': $typography-italic-body,
+ 'largeBody': $typography-italic-large-body,
+ 'H6': $typography-italic-h6,
+ 'H5': $typography-italic-h5,
+ 'H4': $typography-italic-h4,
+ 'H3': $typography-italic-h3,
+ 'H2': $typography-italic-h2,
+ 'H1': $typography-italic-h1,
+ 'display': $typography-italic-display
+ ),
+ 'textCase': (
+ 'none': $typography-text-case-none
+ ),
+ 'textDecoration': (
+ 'none': $typography-text-decoration-none
+ ),
+ 'paragraphIndent': (
+ '0': $typography-paragraph-indent-0
+ ),
+ 'color': (
+ 'primary': $typography-color-primary,
+ 'secondary': $typography-color-secondary,
+ 'placeholder': $typography-color-placeholder,
+ 'disabled': $typography-color-disabled,
+ 'link': $typography-color-link,
+ 'danger': $typography-color-danger,
+ 'primaryInvert': $typography-color-primary-invert,
+ 'secondaryInvert': $typography-color-secondary-invert
+ )
+ ),
+ 'theme': (
+ 'gold': (
+ '10': $theme-gold-10,
+ '20': $theme-gold-20,
+ '30': $theme-gold-30,
+ '40': $theme-gold-40,
+ '50': $theme-gold-50,
+ '60': $theme-gold-60,
+ '70': $theme-gold-70,
+ '80': $theme-gold-80,
+ '90': $theme-gold-90,
+ '100': $theme-gold-100
+ ),
+ 'blue': (
+ '10': $theme-blue-10,
+ '20': $theme-blue-20,
+ '30': $theme-blue-30,
+ '40': $theme-blue-40,
+ '50': $theme-blue-50,
+ '60': $theme-blue-60,
+ '70': $theme-blue-70,
+ '80': $theme-blue-80,
+ '90': $theme-blue-90,
+ '100': $theme-blue-100
+ ),
+ 'gray': (
+ '10': $theme-gray-10,
+ '20': $theme-gray-20,
+ '30': $theme-gray-30,
+ '40': $theme-gray-40,
+ '50': $theme-gray-50,
+ '60': $theme-gray-60,
+ '70': $theme-gray-70,
+ '80': $theme-gray-80,
+ '90': $theme-gray-90,
+ '100': $theme-gray-100,
+ '110': $theme-gray-110,
+ 'white': $theme-gray-white
+ ),
+ 'primaryBlue': $theme-primary-blue,
+ 'primaryGold': $theme-primary-gold
+ )
+);
diff --git a/frontend/_quarto.yml b/frontend/_quarto.yml
index b4f2c35..1e050ec 100644
--- a/frontend/_quarto.yml
+++ b/frontend/_quarto.yml
@@ -1,53 +1,7 @@
project:
- type: website
- resources:
- - "*.html"
- - "*.pdf"
-
-website:
- title: "Air quality advisories"
- repo-url: https://github.com/bcgov/nr-epd-aq-statements
- repo-actions: [source, issue]
- navbar:
- search: true
- right:
- - index.qmd
- - text: Historical advisories
- href: statements.qmd
- - measuring.qmd
- sidebar:
- style: "docked"
- alignment: left
- border: true
- contents:
- - section: Active advisories
- contents: statements/*
- - section: Air Quality Data
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality
- contents:
- - text: Air advisories
- href: index.qmd
- - text: Historical advisories
- href: statements.qmd
- - text: Air Quality Subscription Service
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality/air-advisories/air-quality-subscription-service
- - text: "Air Quality Health Index"
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality/aqhi
- - text: "Air Quality Health Index widget"
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality/aghi-widget
- - text: "Latest air quality data"
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality/current-air-quality-data
- - text: "How we measure"
- href: https://www2.gov.bc.ca/gov/content/environment/air-land-water/air/air-quality/measuring
-
-format:
- html:
- theme: cosmo
- css: styles.css
- toc: true
-
-
-
-
-
-
+ type: bcds
+ pre-render:
+ - construct-lists.py
+ preview:
+ port: 3000
+ browser: false
diff --git a/frontend/_ssb_listing.ejs b/frontend/_ssb_listing.ejs
new file mode 100644
index 0000000..0accdac
--- /dev/null
+++ b/frontend/_ssb_listing.ejs
@@ -0,0 +1,26 @@
+```{=html}
+<% if (items.length > 0) { %>
+
+<% } else { %>
+ There are currently no smoky skies bulletins in effect.
+<% } %>
+```
diff --git a/frontend/construct-lists.py b/frontend/construct-lists.py
new file mode 100644
index 0000000..3ebf712
--- /dev/null
+++ b/frontend/construct-lists.py
@@ -0,0 +1,89 @@
+import datetime
+import os
+import re
+
+import yaml
+
+"""
+This script generates two files as part of the pre-render process for quarto site generation
+
+RECENTS_FILE_NAME will contain a yaml list of files with a date attribute newer than RECENT_THRESHOLD_DAYS
+
+SSB_FILE_NAME will contain any with an `ssb` meta attribute set to True
+
+These are then used in custom listings within the qmd markup
+"""
+
+# editable -- consider posts less than RECENT_THRESHOLD_DAYS days old to be "recent"
+# Hardcoded below to 1 day for smoky skies bulletins with ice = Issue metadata
+RECENT_THRESHOLD_DAYS = 5
+RECENTS_FILE_NAME = '_recent_statements.yaml'
+
+SSB_FILE_NAME = '_ssb.yaml'
+
+# globals. do not modify.
+INPUT_FILES = os.getenv('QUARTO_PROJECT_INPUT_FILES').split("\n")
+HEADER_REGEX = re.compile('^---\n((.*\n)+)---\n', re.MULTILINE)
+
+RECENT_STATEMENTS = []
+SMOKY_SKIES_BULLETINS = []
+
+
+def process_input_files():
+ for f in INPUT_FILES:
+ if not f:
+ continue # skip empty input lines
+
+ print("processing input file: {file}".format(file=f))
+
+ with open(f, 'r') as file:
+ contents = file.read()
+ match = HEADER_REGEX.search(contents)
+ if match:
+ doc_preamble = match.group(1)
+ parsed_header = yaml.safe_load(doc_preamble)
+
+ # what goes in the generated yaml
+ entry_from_header = {
+ 'path': f,
+ 'title': parsed_header['title'],
+ 'type': parsed_header['type'] if 'type' in parsed_header else 'N/A',
+ 'ice': parsed_header['ice'] if 'ice' in parsed_header else 'N/A',
+ 'date': parsed_header['date'] if 'date' in parsed_header else None,
+ 'location': parsed_header['location'] if 'location' in parsed_header else None,
+ }
+
+ if 'type' in parsed_header and parsed_header['type'].lower() == 'ssb':
+ if 'date' in parsed_header:
+ age = (datetime.date.today() - parsed_header['date']).days
+ threshold = RECENT_THRESHOLD_DAYS
+
+ if 'ice' in parsed_header and parsed_header['ice'].lower() == 'issue':
+ threshold = 1 # Only 1 day for SSB with ice = Issue
+
+ if age < threshold:
+ SMOKY_SKIES_BULLETINS.append(entry_from_header)
+
+ # not mutually exclusive with ssb
+ if 'date' in parsed_header:
+ skip = False
+
+ # uncomment this stanza to exclude SSB from recent statements list
+ # if 'type' in parsed_header and parsed_header['type'].lower() == 'ssb':
+ # skip = True
+
+ if not skip:
+ age = (datetime.date.today() - parsed_header['date']).days
+ if age < RECENT_THRESHOLD_DAYS:
+ RECENT_STATEMENTS.append(entry_from_header)
+
+
+print(yaml.safe_dump(INPUT_FILES))
+
+process_input_files()
+
+with open(RECENTS_FILE_NAME, 'w') as output_file:
+ yaml.safe_dump(RECENT_STATEMENTS, output_file)
+
+with open(SSB_FILE_NAME, 'w') as output_file:
+ yaml.safe_dump(SMOKY_SKIES_BULLETINS, output_file)
diff --git a/frontend/index.qmd b/frontend/index.qmd
index 74bc88d..465a626 100644
--- a/frontend/index.qmd
+++ b/frontend/index.qmd
@@ -1,30 +1,29 @@
---
title: "Air quality advisories"
+
+listing:
+ - id: aq-statements
+ template: _aqs_listing.ejs
+ contents:
+ - ./_recent_statements.yaml
+ sort: "date desc"
+ - id: ssb
+ template: _ssb_listing.ejs
+ contents:
+ - ./_ssb.yaml
+ sort: "date desc"
---
## Issued advisories
-
-
-There are currently no air quality advisories in effect.
-
-OR
-
-| Location | Advisory Type | Status | Date |
-| --- | --- | --- | --- |
-| Entry | Entry | Entry | Entry |
-
-
-
+::: {#aq-statements}
+:::
## Current Smoky Skies Bulletin map
-
+::: {#ssb}
+:::
## Latest updates
diff --git a/frontend/requirements.txt b/frontend/requirements.txt
new file mode 100644
index 0000000..8aea057
--- /dev/null
+++ b/frontend/requirements.txt
@@ -0,0 +1,17 @@
+attrs==25.1.0
+fastjsonschema==2.21.1
+jsonschema==4.23.0
+jsonschema-specifications==2024.10.1
+jupyter_client==8.6.3
+jupyter_core==5.7.2
+nbclient==0.10.2
+nbformat==5.10.4
+platformdirs==4.3.6
+python-dateutil==2.9.0.post0
+PyYAML==6.0.2
+pyzmq==26.2.0
+referencing==0.36.2
+rpds-py==0.22.3
+six==1.17.0
+tornado==6.4.2
+traitlets==5.14.3
diff --git a/frontend/shortcode_demos.qmd b/frontend/shortcode_demos.qmd
new file mode 100644
index 0000000..490ee26
--- /dev/null
+++ b/frontend/shortcode_demos.qmd
@@ -0,0 +1,192 @@
+---
+title: "Shortcode Demonstrations"
+---
+
+## Callouts
+
+::: {.callout-note}
+Quarto default callout
+:::
+
+{{< callout_start >}}
+BCDS callout - sample text, defaults
+{{< callout_end >}}
+
+{{< callout_start title="Titled Callout" >}}
+BCDS callout - sample text, default variant
+{{< callout_end >}}
+
+{{< callout_start variant="Blue">}}
+BCDS callout - sample text, blue variant
+{{< callout_end >}}
+
+{{< callout_start variant="lightBlue" title="Titled, variant">}}
+BCDS callout - sample text, light blue variant, with title
+{{< callout_end >}}
+
+## Accordions
+
+{{< accordion_controls >}}
+
+{{< accordion_start title="Accordion Example - Continuous Monitoring" >}}
+In continuous monitoring, air quality is constantly measured by drawing air in through various tubes that are connected to data loggers that automatically transmit the data to a central MOE database. The data is automatically checked for errors and is published hourly on the Current Air Data page, where it is available to the public, and used to calculate the Air Quality Health Index (AQHI).
+
+This data is useful for providing real-time information and for calculating indexes. However, until it goes through the MOE's data validation process, it is still considered raw data and should not be used for comparing to air quality guidelines or objectives.
+
+The province uses a wide range of automated instruments to collect and measure air quality data, including the following (with instrument acronyms and parameters measured in parenthesis):
+
+- Tempered Element Oscillating Microbalance (TEOM) (PM2.5 and PM10)
+- Beta AttenuatedMonitoring (BAM) (PM2.5)
+- UV Photometry (ozone)
+- Chemiluminscence (nitrogen dioxide)
+- UV Fluorescence (sulphur dioxide)
+- Nondispersive Infrared Photometry (carbon monoxide)
+- Pulsed Fluorescence (total reduced sulphur or H2S)
+{{< accordion_end >}}
+
+{{< accordion_start title="Accordion Example - Initially Open" initiallyOpen="true">}}
+In continuous monitoring, air quality is constantly measured by drawing air in through various tubes that are connected to data loggers that automatically transmit the data to a central MOE database. The data is automatically checked for errors and is published hourly on the Current Air Data page, where it is available to the public, and used to calculate the Air Quality Health Index (AQHI).
+
+This data is useful for providing real-time information and for calculating indexes. However, until it goes through the MOE's data validation process, it is still considered raw data and should not be used for comparing to air quality guidelines or objectives.
+
+The province uses a wide range of automated instruments to collect and measure air quality data, including the following (with instrument acronyms and parameters measured in parenthesis):
+
+- Tempered Element Oscillating Microbalance (TEOM) (PM2.5 and PM10)
+- Beta AttenuatedMonitoring (BAM) (PM2.5)
+- UV Photometry (ozone)
+- Chemiluminscence (nitrogen dioxide)
+- UV Fluorescence (sulphur dioxide)
+- Nondispersive Infrared Photometry (carbon monoxide)
+- Pulsed Fluorescence (total reduced sulphur or H2S)
+{{< accordion_end >}}
+
+{{< accordion_start title="Accordion Example - Initially Open, Header" initiallyOpen="true" headerClass="h3">}}
+In continuous monitoring, air quality is constantly measured by drawing air in through various tubes that are connected to data loggers that automatically transmit the data to a central MOE database. The data is automatically checked for errors and is published hourly on the Current Air Data page, where it is available to the public, and used to calculate the Air Quality Health Index (AQHI).
+
+This data is useful for providing real-time information and for calculating indexes. However, until it goes through the MOE's data validation process, it is still considered raw data and should not be used for comparing to air quality guidelines or objectives.
+
+The province uses a wide range of automated instruments to collect and measure air quality data, including the following (with instrument acronyms and parameters measured in parenthesis):
+
+- Tempered Element Oscillating Microbalance (TEOM) (PM2.5 and PM10)
+- Beta AttenuatedMonitoring (BAM) (PM2.5)
+- UV Photometry (ozone)
+- Chemiluminscence (nitrogen dioxide)
+- UV Fluorescence (sulphur dioxide)
+- Nondispersive Infrared Photometry (carbon monoxide)
+- Pulsed Fluorescence (total reduced sulphur or H2S)
+{{< accordion_end >}}
+
+
+{{< accordion_start title="Accordion Example - Header" headerClass="h3">}}
+In continuous monitoring, air quality is constantly measured by drawing air in through various tubes that are connected to data loggers that automatically transmit the data to a central MOE database. The data is automatically checked for errors and is published hourly on the Current Air Data page, where it is available to the public, and used to calculate the Air Quality Health Index (AQHI).
+
+This data is useful for providing real-time information and for calculating indexes. However, until it goes through the MOE's data validation process, it is still considered raw data and should not be used for comparing to air quality guidelines or objectives.
+
+The province uses a wide range of automated instruments to collect and measure air quality data, including the following (with instrument acronyms and parameters measured in parenthesis):
+
+- Tempered Element Oscillating Microbalance (TEOM) (PM2.5 and PM10)
+- Beta AttenuatedMonitoring (BAM) (PM2.5)
+- UV Photometry (ozone)
+- Chemiluminscence (nitrogen dioxide)
+- UV Fluorescence (sulphur dioxide)
+- Nondispersive Infrared Photometry (carbon monoxide)
+- Pulsed Fluorescence (total reduced sulphur or H2S)
+{{< accordion_end >}}
+
+
+## Cards
+
+{{< card_start title="Titled Card">}}
+card contents
+{{< card_end >}}
+
+{{< card_start title="Red Card" variant="danger">}}
+danger card contents
+{{< card_end >}}
+
+{{< card_start title="Red Card" variant="danger" useIcons="true">}}
+danger card contents, icon
+{{< card_end >}}
+
+{{< card_start title="Success Card" variant="success">}}
+success card contents
+{{< card_end >}}
+
+{{< card_start title="Success Card" variant="success" useIcons="true">}}
+success card contents, icons
+{{< card_end >}}
+
+{{< card_start title="Info Card" variant="info">}}
+info card contents
+{{< card_end >}}
+
+{{< card_start title="Info Card" variant="info" useIcons="true">}}
+info card contents, icons enabled
+{{< card_end >}}
+
+{{< card_start title="Warning Card" variant="warning">}}
+warning card contents
+{{< card_end >}}
+
+{{< card_start title="Warning Card" variant="warning" useIcons="true">}}
+warning card contents, icons enabled
+{{< card_end >}}
+
+
+{{< card_start title="Info Image Card" variant="info" logo="/_extensions/bcds/assets/BCID_V_rgb_pos.png" >}}
+info with logo card contents
+{{< card_end >}}
+
+{{< card_start title="Info Image Card" variant="info" logo="/_extensions/bcds/assets/BCID_V_rgb_pos.png" >}}
+info with logo card contents
+
+[Link Example](https://github.com/bcgov/nr-epd-aq-statements/)
+{{< card_end >}}
+
+{{< card_start title="Warning Image Card" variant="warning" logo="/_extensions/bcds/assets/BCID_V_rgb_pos.png" useIcons="true" >}}
+Warning with logo card contents and icons
+
+[Link Example](https://github.com/bcgov/nr-epd-aq-statements/)
+{{< card_end >}}
+
+{{< card_start logo="/_extensions/bcds/assets/BCID_V_rgb_pos.png" >}}
+untitled with logo card contents
+{{< card_end >}}
+
+{{< card_start variant="danger">}}
+untitled danger card with logo card contents
+{{< card_end >}}
+
+{{< card_start>}}
+untitled card contents
+{{< card_end >}}
+
+## Inline Alerts
+
+{{< inline_alert_start title="Titled Alert">}}
+Alert contents
+{{< inline_alert_end >}}
+
+{{< inline_alert_start>}}
+Untitled alert contents
+{{< inline_alert_end >}}
+
+{{< inline_alert_start variant="warning">}}
+Untitled alert contents, warning
+{{< inline_alert_end >}}
+
+{{< inline_alert_start variant="warning">}}
+Untitled alert contents, warning
+{{< inline_alert_end >}}
+
+{{< inline_alert_start variant="danger">}}
+Untitled alert contents, danger
+{{< inline_alert_end >}}
+
+{{< inline_alert_start variant="success">}}
+Untitled alert contents, success
+{{< inline_alert_end >}}
+
+{{< inline_alert_start variant="info">}}
+Untitled alert contents, info (default)
+{{< inline_alert_end >}}
diff --git a/frontend/statements.qmd b/frontend/statements.qmd
index 13e240b..043d33a 100644
--- a/frontend/statements.qmd
+++ b/frontend/statements.qmd
@@ -7,7 +7,7 @@ listing:
title: "Advisory Type"
location: "Location"
author: "Contact"
- contents: "statements/*qmd"
+ contents: "statements/**/*md"
---
diff --git a/frontend/statements/2024-08-21-smoky-skies-issue.pdf b/frontend/statements/2024-08-21-smoky-skies-issue.pdf
deleted file mode 100644
index 43f7797..0000000
Binary files a/frontend/statements/2024-08-21-smoky-skies-issue.pdf and /dev/null differ
diff --git a/frontend/statements/2024-08-21-smoky-skies-issue.qmd b/frontend/statements/2024-08-21-smoky-skies-issue.qmd
deleted file mode 100644
index 3cf1c13..0000000
--- a/frontend/statements/2024-08-21-smoky-skies-issue.qmd
+++ /dev/null
@@ -1,80 +0,0 @@
----
-title: "Smokey Skies Bulletin"
-location: "Elkford"
-date: 2024-08-21
-categories:
-author:
- - name: FName LName
- title: Senior Air Quality Meteorologist
- phone: 250-645-9358
----
-
-[PDF](2024-08-21-smoky-skies-issue.pdf)
-[HTML](2024-08-21-smoky-skies-issue.html)
-
-The Regions of BC highlighted on the map are being impacted or are likely to be impacted by wildfire smoke over the next 24-48 hours.
-
-Local smoke near active wildfires. Smoke levels vary across the southern half of the province because of localized rain and changing wind conditions.
-
-The next bulletin update will be available July 26, 2024.
-
-The bulletin can be accessed online at [gov.bc.ca/airqualityadvisories](https://www.gov.bc.ca/airqualityadvisories).
-
-## Be informed
-
-During a wildfire, smoke conditions can change quickly over short distances and can vary considerably hour-by-hour.
-
-Wildfire smoke is a natural part of our environment but it is important to be mindful that exposure to smoke may affect your health.
-
-People with pre-existing health conditions, respiratory infections such as COVID-19, older adults, pregnant women and infants, children, and sensitive individuals are more likely to experience health effects from smoke exposure.
-
-## During smoky conditions
-
-**Follow your common sense**
-
-- Stop or reduce your activity level if breathing becomes uncomfortable or you feel unwell.
-- Stay cool and drink plenty of fluids.
-- If you have asthma or other chronic illness, carry any rescue (fast-acting) medications with you at all times and activate your personal care plan that has been designed with your family physician.
-- Make sure that children and others who cannot care for themselves follow the same advice.
-
-**Monitor your symptoms**
-
-- People respond differently to smoke. Mild irritation and discomfort are common, and usually disappear when the smoke clears.
-- If you are unsure whether you need medical care, call HealthLink BC at 8-1-1.
-- If you are experiencing difficulty in breathing, chest pain or discomfort, or a severe cough, contact your health care provider, walk-in clinic, or emergency department. If you are having a medical emergency, call 9-1-1.
-
-**Tips to reduce your smoke exposure**
-
-- Smoke levels may be lower indoors but will still be elevated, so stay aware of your symptoms even when you are indoors.
-- Running a commercially available HEPA (high efficiency particulate air) filter can improve indoor air quality in the room where the device is located.
-- If you have a forced air heating/cooling system in your home, it may help to change the
-filter and set the fan to run continuously.
-- Reduce indoor air pollution sources such as smoking, burning incense, and frying foods.
-- Consider going to a library, community center, or shopping mall with cooler filtered air to get some relief from the smoke.
-- If travelling in a car with air conditioning, keep the windows up and the ventilation set to recirculate.
-- If you are very sensitive to smoke, consider moving to another location with cleaner air, but be aware that conditions can change rapidly.
-- Maintaining good overall health is a good way to prevent health effects resulting from short-term exposure to air pollution.
-
-## More information
-
-**For additional general information about wildfire smoke and air quality:**
-
-**For additional general information about wildfire smoke and your health:**
-
-## Contact information
-
-**Media and public inquiries regarding air quality and this bulletin:**
-
-{{< meta contact.1.name >}}, {{< meta contact.1.title >}}, {{< meta contact.1.phone >}}
-
-**Media questions regarding health implications of wildfires:**
-
-## Regions included under this bulletin
-
-**B.C. North Peace River** includes the City of Fort St John and all communities along Hwy 97 extending from Farmington to Pink Mountain; Hwy 29 including Moberly Lake, Hudson’s Hope; also includes Rolla, Clayhurst and Goodlow.
-
-**B.C. South Peace River** includes Chetwynd, Dawson Creek, Pouce Coupe, and Tumbler Ridge.
-
-**Stuart - Nechako** includes Fraser Lake, Fort Fraser, Vanderhoof, Fort St James, and Tachie.
-
-**Williston** includes McLeod Lake, Mackenzie and Williston Lake.
\ No newline at end of file
diff --git a/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.md b/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.md
new file mode 100644
index 0000000..c251e28
--- /dev/null
+++ b/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.md
@@ -0,0 +1,199 @@
+---
+date: 2025-02-06
+editor: visual
+ice: Issue
+nextBulletinDateString: 2025-02-07
+params:
+ ice: Issue
+ sel_aqMet: Sakshi
+ selRegionsIDs:
+ - Watson Lake
+ - Prince George
+ - Whistler
+ smokeMessage: "`24-48 hours`"
+sel_aqMet: Vickie
+selRegionsIDS: Bulkley Valley, Lakes District, Chilcotin, Stuart -
+ Nechako
+smokeMessage: "`24-48 hours`"
+title: Smoky Skies Bulletin
+toc-title: Table of contents
+type: SSB
+---
+
+The Regions of BC highlighted on the map are being impacted or are
+likely to be impacted by wildfire smoke over the next `24-48 hours`.
+
+The next bulletin update will be available February 07, 2025.
+
+The bulletin can be accessed online at
+.
+
+{fig-alt="add interactive html map and adaptive alt text based on regions selected"
+width="75%" height="500"}
+
+# Be informed
+
+During a wildfire, smoke conditions can change quickly over short
+distances and can vary considerably hour-by-hour.
+
+Wildfire smoke is a natural part of our environment but it is important
+to be mindful that exposure to smoke may affect your health.
+
+People with pre-existing health conditions, respiratory infections such
+as COVID-19, older adults, pregnant women and infants, children, and
+sensitive individuals are more likely to experience health effects from
+smoke exposure.
+
+# During smoky conditions
+
+## Follow your common sense
+
+- Stop or reduce your activity level if breathing becomes
+ uncomfortable or you feel unwell.
+
+- Stay cool and drink plenty of fluids.
+
+- If you have asthma or other chronic illness, carry any rescue
+ (fast-acting) medications with you at all times and activate your
+ personal care plan that has been designed with your family
+ physician.
+
+Make sure that children and others who cannot care for themselves follow
+the same advice.
+
+## Monitor your symptoms
+
+- People respond differently to smoke. Mild irritation and discomfort
+ are common, and usually disappear when the smoke clears.
+
+- If you are unsure whether you need medical care, call HealthLink BC
+ at 8-1-1.
+
+- If you are experiencing difficulty in breathing, chest pain or
+ discomfort, or a severe cough, contact your health care provider,
+ walk-in clinic, or emergency department. If you are having a medical
+ emergency, call 9-1-1.
+
+## Tips to reduce your smoke exposure
+
+- Smoke levels may be lower indoors but will still be elevated, so
+ stay aware of your symptoms even when you are indoors.
+
+- Running a commercially available HEPA (high efficiency particulate
+ air) filter can improve indoor air quality in the room where the
+ device is located.
+
+- If you have a forced air heating/cooling system in your home, it may
+ help to change the filter and set the fan to run continuously.
+
+- Reduce indoor air pollution sources such as smoking, burning
+ incense, and frying foods.
+
+- Consider going to a library, community center, or shopping mall with
+ cooler filtered air to get some relief from the smoke.
+
+- If travelling in a car with air conditioning, keep the windows up
+ and the ventilation set to recirculate.
+
+- If you are very sensitive to smoke, consider moving to another
+ location with cleaner air, but be aware that conditions can change
+ rapidly.
+
+- Maintaining good overall health is a good way to prevent health
+ effects resulting from short-term exposure to air pollution.
+
+# More information
+
+## For additional general information about wildfire smoke and air quality:
+
+Advisories for Metro Vancouver and the Fraser Valley Regional District
+
+- Metro Vancouver's Air Quality Data and Advisories page:
+ .
+
+Air Quality Data
+
+- Air Quality Health Index:
+ [gov.bc.ca/airqualityhealthindex](https://www.env.gov.bc.ca/epd/bcairquality/data/aqhi-table.html).
+
+- Air Quality Map:
+ [gov.bc.ca/airqualitymap](https://www.env.gov.bc.ca/epd/bcairquality/readings/find-stations-map.html).
+
+Wildfire smoke forecasts
+
+- Environment and Climate Change Canada FireWork prediction system:
+ .
+
+- BlueSky Canada smoke forecasting system:
+ .
+
+## For additional general information about wildfire smoke and your health:
+
+BC Centre for Disease Control
+
+- Wildfire smoke and your health:
+
+
+Provincial Health Authorities
+
+- First Nations Health Authority:
+
+
+HealthLink BC
+
+- Wildfires and your health:
+
+
+- Phone 8-1-1 (toll free, 24 hours a day, 7 days a week)
+
+Worksafe BC
+
+- For information about working outdoors during smoky conditions, see
+ the wildfire FAQ website:
+
+
+# Contact information
+
+## Media and public inquiries regarding air quality and this bulletin:
+
+Vickie Irish, Air Quality Meteorologist, Ministry of Environment and
+Parks, 778-584-5719.
+
+## Media questions regarding health implications of wildfires:
+
+First Nations Health Authority Environmental Public Health Services or
+Main FNHA Line: 604-693-6500 After Hours: 1-844-666-0711 Email:
+Ephs.afterhours@fnha.ca Media line: 604-831-4898
+
+Interior Health Authority Media line: 1-844-469-7077 Email:
+media@interiorhealth.ca
+
+Northern Health Authority Media Line: 1-877-961-7724
+
+Vancouver Coastal Health Authority Rachel Galligan, Communications
+Leader: 236-833-5618 Deana Lancaster, Communications Leader:
+604-230-6130 After-hours media line: 604-202-2012
+
+# Regions included under this bulletin
+
+- Bulkley Valley: includes Hazelton, Smithers, Telkwa, and Granisle.
+- Chilcotin: includes Hansville, Alexis Creek, Chilanko Forks, Tatla
+ Lake, Nimpo Lake, Anahim Lake, and the southern half of Tweedsmuir
+ Park.
+- Lakes District: includes Houston, Topley, Burns Lake, and Endako.
+- Stuart - Nechako: includes Fraser Lake, Fort Fraser, Vanderhoof,
+ Fort St James, and Tachie.
+
+# Quarto Passthrough Tests
+
+::: content-block
+Content-block passthrough test
+:::
+
+{{< callout_start variant=Blue >}} Shortcodes to be passed through
+(escaped) and used in second-stage render must use 1 extra curly brace
+on opening and closing {{< callout_end >}}
+
diff --git a/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.pdf b/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.pdf
new file mode 100644
index 0000000..2946ab7
Binary files /dev/null and b/frontend/statements/2025-02/06/2025-02-06_Smoky-Skies_Issue.pdf differ
diff --git a/frontend/statements/2025-02/06/map.html b/frontend/statements/2025-02/06/map.html
new file mode 100644
index 0000000..d429985
--- /dev/null
+++ b/frontend/statements/2025-02/06/map.html
@@ -0,0 +1,4079 @@
+
+
+
+
+leaflet
+
+
+
+
+
+
+
+
+
+
+
+
+
+