diff --git a/packages/pxweb2/index.html b/packages/pxweb2/index.html
index a5508f3f6..e882cd56a 100644
--- a/packages/pxweb2/index.html
+++ b/packages/pxweb2/index.html
@@ -1,8 +1,8 @@
-
+
- Pxweb2
+ Statistikkbanken – SSB
@@ -14,6 +14,7 @@
/>
+
+
+
+
+
+
+
diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js
index 87ec89910..d42ff0a0b 100644
--- a/packages/pxweb2/public/config/config.js
+++ b/packages/pxweb2/public/config/config.js
@@ -1,21 +1,36 @@
-globalThis.PxWeb2Config = {
+window.PxWeb2Config = {
language: {
supportedLanguages: [
+ { shorthand: 'no', languageName: 'Norsk' },
{ shorthand: 'en', languageName: 'English' },
- { shorthand: 'sv', languageName: 'Svenska' },
],
- defaultLanguage: 'en',
+ defaultLanguage: 'no',
fallbackLanguage: 'en',
- showDefaultLanguageInPath: true,
- positionInPath: 'after',
+ showDefaultLanguageInPath: false,
+ positionInPath: 'before',
},
baseApplicationPath: '/',
- apiUrl: 'https://api.scb.se/OV0104/v2beta/api/v2',
+ apiUrl: 'https://data.ssb.no/api/pxwebapi/v2',
maxDataCells: 150000,
- useDynamicContentInTitle: false,
- showBreadCrumbOnStartPage: false,
+ showBreadCrumbOnStartPage: true,
specialCharacters: ['.', '..', ':', '-', '...', '*'],
variableFilterExclusionList: {
+ no: [
+ 'statistikkvariabel',
+ 'år',
+ 'kvartal',
+ 'måned',
+ 'uke',
+ 'driftsår',
+ 'enkeltår',
+ 'intervall (år)',
+ 'halvår',
+ 'kvartal (u)',
+ 'termin',
+ 'toårlig',
+ 'fireårlig',
+ 'femårlig',
+ ],
en: [
'observations',
'year',
@@ -23,6 +38,18 @@ globalThis.PxWeb2Config = {
'month',
'every other year',
'every fifth year',
+ 'contents',
+ 'half year',
+ 'quarter (u)',
+ 'termin',
+ 'week',
+ 'single years',
+ 'biannual',
+ 'every 4th year',
+ 'every 5th year',
+ 'interval (year)',
+ 'driftsår',
+ 'every ten years',
],
sv: [
'tabellinnehåll',
@@ -35,7 +62,7 @@ globalThis.PxWeb2Config = {
],
},
homePage: {
- sv: '', // Set to your Swedish homepage URL
- en: '', // Set to your English homepage URL
+ no: '/',
+ en: '/en/',
},
};
diff --git a/packages/pxweb2/ssbscript.js b/packages/pxweb2/ssbscript.js
new file mode 100644
index 000000000..a53ee3e8d
--- /dev/null
+++ b/packages/pxweb2/ssbscript.js
@@ -0,0 +1,148 @@
+(function () {
+ const WIP_STATUS_TEXT = {
+ en: {
+ before:
+ "Welcome to the new Statbank! We're still fine-tuning things. If something is missing, you can still ",
+ linkText: 'access the old version',
+ },
+ no: {
+ before:
+ 'Velkommen til nye Statistikkbanken! Vi jobber med de siste detaljene. Skulle du savne noe, kan du fortsatt ',
+ linkText: 'bruke den gamle løsningen',
+ },
+ };
+
+ function checkLanguage() {
+ const url = new URL(globalThis.location.href);
+ if (url.pathname.includes('/en/')) {
+ return 'en';
+ } else {
+ return 'no';
+ }
+ }
+ /**
+ * Fires update() on initial load and on every SPA navigation:
+ * - history.pushState / replaceState (programmatic navigation)
+ * - popstate (back/forward)
+ */
+ function computeTransformedUrl() {
+ const url = new URL(globalThis.location.href);
+ const newUrl = url.pathname.replace('/statbank', '/statbank1');
+ return newUrl.toString();
+ }
+ // ---------------------------------------------
+ // Track last language to detect language changes
+ // ---------------------------------------------
+ let lastLang = null;
+
+ function update() {
+ const bodyLong = document.querySelector(
+ '#wip-status-message div[class*="bodylong"]',
+ );
+ if (!bodyLong) {
+ return;
+ }
+
+ const language = checkLanguage();
+ const content = WIP_STATUS_TEXT[language] || WIP_STATUS_TEXT.no;
+ const before = document.createTextNode(content.before);
+ const newUrl = computeTransformedUrl();
+ const linkId = 'myLink';
+
+ const link = document.createElement('a');
+ link.id = linkId;
+ link.className = 'oldLinkClass';
+ link.href = newUrl;
+ link.textContent = content.linkText;
+ link.target = '_blank'; // open in new tab
+ link.rel = 'noopener noreferrer'; // safe when target=_blank
+
+ const after = document.createTextNode('.');
+ bodyLong.textContent = '';
+
+ bodyLong.appendChild(before);
+ bodyLong.appendChild(link);
+ bodyLong.appendChild(after);
+ // ---------------------------------------------
+ // Cookie Banner Language Sync
+ // ---------------------------------------------
+ if (language !== lastLang) {
+ // Language changed => reinitialize banner content
+ const content = bannerContent[language];
+ initCookieConsent(content);
+ lastLang = language;
+ }
+ }
+
+ function scheduleUpdate() {
+ update();
+ requestAnimationFrame(update);
+ setTimeout(update, 400);
+ }
+
+ // Patch pushState / replaceState to emit a custom event
+ ['pushState', 'replaceState'].forEach((method) => {
+ const original = history[method];
+ history[method] = function () {
+ const ret = original.apply(this, arguments);
+ globalThis.dispatchEvent(new Event('rr-nav')); // custom event for SPA navigation
+ return ret;
+ };
+ });
+
+ // --- Persistent link binder setup ---
+ let boundResetLink = null;
+
+ function onResetConsentClick(e) {
+ e.preventDefault();
+ if (typeof resetCookieConsent === 'function') {
+ resetCookieConsent();
+ }
+ }
+
+ function bindResetConsentLink() {
+ const language = checkLanguage();
+ const link = document.querySelector(
+ `a[href$="change-cookie-consent-${language}"]`,
+ );
+
+ if (boundResetLink && !boundResetLink.isConnected) {
+ boundResetLink = null;
+ }
+
+ if (link && link !== boundResetLink) {
+ boundResetLink = link;
+ link.addEventListener('click', onResetConsentClick);
+ }
+
+ if (!link) {
+ boundResetLink = null;
+ }
+ }
+
+ // --- No observer: rebind on known lifecycle/navigation points ---
+ function scheduleBindResetConsentLink() {
+ // immediate attempt
+ bindResetConsentLink();
+ // after paint (for SPA updates that render slightly later)
+ requestAnimationFrame(bindResetConsentLink);
+ // delayed fallback (for async content loading)
+ setTimeout(bindResetConsentLink, 600);
+ }
+
+ function onNavigationUpdate() {
+ scheduleBindResetConsentLink();
+ scheduleUpdate();
+ }
+
+ // --- Bind on initial DOM load ---
+ document.addEventListener('DOMContentLoaded', () => {
+ scheduleBindResetConsentLink();
+ scheduleUpdate();
+ });
+
+ // --- Bind on SPA navigation & back/forward ---
+ globalThis.addEventListener('rr-nav', onNavigationUpdate);
+
+ globalThis.addEventListener('popstate', onNavigationUpdate);
+})();
diff --git a/packages/pxweb2/ssbstyle.css b/packages/pxweb2/ssbstyle.css
new file mode 100644
index 000000000..d78c8389c
--- /dev/null
+++ b/packages/pxweb2/ssbstyle.css
@@ -0,0 +1,249 @@
+.container {
+ @media (min-width: 0px) and (max-width: 575px) {
+ padding: 20px 16px;
+ }
+ @media (min-width: 576px) and (max-width: 767px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 768px) and (max-width: 991px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 992px) and (max-width: 1199px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 1200px) and (max-width: 1399px) {
+ padding: 20px 24px;
+ }
+ @media (min-width: 1400px) {
+ padding: 20px 24px;
+ }
+}
+
+/* .oldLinkClass {
+ display: inline-flex;
+ background-color: #c3e6fe;
+ width: 100%;
+ font-family: "PxWeb-font";
+ font-weight: 400;
+ font-size: 1rem;
+ font-style: normal;
+ line-height: 1.75rem;
+ text-decoration: none;
+ color: #162327;
+ gap: 12px;
+} */
+a.oldLinkClass {
+ display: inline;
+ padding: 0px;
+ text-decoration-line: underline;
+ color: #274247;
+}
+a.oldLinkClass:hover {
+ text-decoration: none;
+}
+a.oldLinkClass:focus-visible {
+ outline: 3px solid var(--px-color-border-focus-outline);
+ outline-offset: 5px;
+ box-shadow: 0 0 0 3px var(--px-color-border-focus-boxshadow);
+}
+svg.info-icon {
+ min-width: max-content;
+}
+.info-text {
+ margin-block-start: 0px;
+}
+
+.sr-only {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0 0 0 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+
+/* Cookie banner styling from CMS */
+
+.cookie-banner {
+ position: fixed;
+ bottom: 1.56rem;
+ left: 1.56rem;
+ width: 41.5rem;
+ max-width: calc(100vw - 3.2rem);
+ padding: 1.75rem;
+ background: #00824d;
+ border-radius: 0.5rem;
+ z-index: 2147483647;
+ font-family: PxWeb-font, sans-serif;
+}
+
+.cookie-banner:focus {
+ outline: none;
+}
+
+.cookie-banner-title,
+.cookie-banner-text {
+ color: #fff;
+}
+
+.cookie-banner-title {
+ margin-top: 0;
+ margin-bottom: 0.5rem;
+ font-size: 1.25rem;
+ line-height: 2rem;
+}
+
+.cookie-banner-text {
+ margin-top: 0;
+ margin-bottom: 0.63rem;
+ font-size: 1rem;
+ font-stretch: normal;
+ font-style: normal;
+ font-weight: 400;
+ letter-spacing: normal;
+ line-height: 1.7;
+}
+
+.cookie-banner-buttons {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1.25rem;
+ color: #fff;
+}
+
+.cookie-banner-buttons .cookie-button-accept,
+.cookie-banner-buttons .cookie-button-decline {
+ background: #fff;
+ color: #00824d;
+ margin: 0.12rem 0.12rem 0.12rem 0;
+ border: none;
+ font-size: 1rem;
+ min-height: 2.5rem;
+}
+
+.cookie-banner-buttons
+ :is(.cookie-button-accept, .cookie-button-decline):is(:hover, :focus) {
+ outline-color: #fff;
+ text-decoration: underline;
+}
+
+.cookie-banner-buttons
+ :is(.cookie-button-accept, .cookie-button-decline):hover {
+ cursor: pointer;
+}
+
+.cookie-banner .ssb-link.negative {
+ border-bottom-color: #fff !important;
+ background-image: linear-gradient(120deg, #fff 0%, #fff 100%) !important;
+ color: #b6e8b8;
+}
+
+.cookie-banner .ssb-link.negative .link-text {
+ font-size: 1rem;
+ color: #fff !important;
+}
+
+.cookie-banner .ssb-link.negative:hover,
+.cookie-banner .ssb-link.negative:focus {
+ border-bottom-color: transparent;
+ color: #274247;
+ outline: 0;
+ background-size: 100% 100%;
+}
+
+.cookie-banner .ssb-link.negative:hover .link-text,
+.cookie-banner .ssb-link.negative:focus .link-text {
+ color: #00824d !important;
+}
+
+.ssb-btn {
+ background-color: transparent;
+ border: 2px solid #00824d;
+ border-radius: 2px;
+ color: #00824d;
+ font-family: inherit;
+ font-size: 16px;
+ font-weight: 700;
+ height: -webkit-fit-content;
+ height: -moz-fit-content;
+ height: fit-content;
+ line-height: 1.25;
+ min-height: 40px;
+ padding: 12px 20px;
+ text-align: center;
+ text-underline-position: under;
+ transition:
+ background 0.18s,
+ color 0.18s;
+ white-space: nowrap;
+}
+
+.ssb-btn:focus {
+ outline: 2px solid #9272fc;
+ outline-offset: 2px;
+}
+
+.ssb-link {
+ font-stretch: normal;
+ font-weight: 400;
+ background-image: linear-gradient(120deg, #00824d, #00824d);
+ background-position: 0 100%;
+ background-repeat: no-repeat;
+ background-size: 100% 0;
+ border-bottom: 1px solid #00824d;
+ color: #00824d;
+ cursor: pointer;
+ display: inline;
+ font-size: 16px;
+ line-height: 1.7;
+ margin-top: -3px;
+ margin-bottom: -2px;
+ padding: 0 1px 2px;
+ position: relative;
+ text-decoration: none;
+ transition:
+ background-size 0.2s ease-in,
+ color 0.1s;
+}
+
+@media (max-width: 768px) {
+ .cookie-banner {
+ right: 1rem;
+ left: 1rem;
+ bottom: 1rem;
+ min-width: calc(100vw - 2rem);
+ max-height: calc(100vh - 2rem);
+ flex-direction: column;
+ overflow-y: auto;
+ }
+
+ .cookie-banner-title,
+ .cookie-banner-text,
+ .cookie-banner-link {
+ width: fit-content;
+ }
+
+ .cookie-banner-text {
+ margin-bottom: 1.25rem;
+ }
+
+ .cookie-banner-link {
+ margin-bottom: 1.75rem;
+ }
+
+ .cookie-banner-buttons {
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .cookie-banner-buttons .cookie-button-accept,
+ .cookie-banner-buttons .cookie-button-decline {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ }
+}