rework sidebar

It now works out of the box! No special tinkering required!
This commit is contained in:
buzzcode2007 2024-04-02 23:21:38 +08:00
parent 20da1c32b7
commit 4cebf541c0
3 changed files with 432 additions and 488 deletions

View file

@ -9,26 +9,33 @@
<body> <body>
<main class="dual"> <main class="dual">
<ul id="slide-out" class="sidenav sidenav-fixed" name="control">
<li>
<li for="extension_name" class="flow-text"></li>
</li>
<li>
<div class="input-field">
<input type="search" data-result="filters" data-results-filters="name,url" placeholder=" "></input>
<label for="settings_filters_search_prompt"></label>
</div>
</li>
<div data-results-target="filters">
</div>
</ul>
<!--
<side class="side sidebar"> <side class="side sidebar">
<nav class="nav-wrapper">
<ul class="left">
<li for="extension_name" class="flow-text"></li>
</ul>
<ul class="hide-on-med-and-up right">
<li><a data-icon="chevron-left" data-action="ui,close,navbar"></a></li>
</ul>
</nav>
<div class="input-field outlined"> <div class="input-field outlined">
<input type="search" data-result="filters" placeholder=" "></input> <input type="search" data-result="filters" placeholder=" "></input>
<label for="settings_filters_search_prompt"></label> <label for="settings_filters_search_prompt"></label>
</div> </div>
<div class="collection" data-results-target="filters" data-results-filters="url"> <div class="collection" data-results-target="filters" >
</div> </div>
</side> </side>-->
<section> <section>
<nav class="nav-wrapper"> <nav class="nav-wrapper">
<ul class="left"> <ul class="left">
<li><a class="hide-on-med-and-up" data-icon="menu" data-action="ui,open,navbar"></a></li> <li><a class="hide-on-med-and-up" data-icon="menu" works-sidebar="control"></a></li>
</ul> </ul>
<ul class="right"> <ul class="right">
<li><a data-icon="trash-can" data-action="filters,delete,one"></a></li> <li><a data-icon="trash-can" data-action="filters,delete,one"></a></li>

View file

@ -7,493 +7,466 @@ import texts from "./read.js";
let DEBUG = false; let DEBUG = false;
export default class windowman { export default class windowman {
static new(URL, height, width) { static new(URL, height, width) {
this.window = chrome.windows.create({ this.window = chrome.windows.create({
url: chrome.runtime.getURL(URL), url: chrome.runtime.getURL(URL),
type: "popup", type: "popup",
width: width ? parseInt(width) : 600, width: width ? parseInt(width) : 600,
height: height ? parseInt(height) : 600, height: height ? parseInt(height) : 600,
}); });
}
// Prepare the window with its metadata.
constructor() {
function headers() {
let LOAD_STATE = true;
let UI = {
CSS: [
chrome.runtime.getURL("gui/styles/external/fonts/materialdesignicons.min.css"),
chrome.runtime.getURL("gui/styles/external/materialize/css/materialize.css"),
chrome.runtime.getURL("gui/styles/ui.css"),
]
};
for (let index = 0; index < UI[`CSS`].length; index++) {
const source = UI.CSS[index];
try {
(async () => {
// Import source reading for later.
const reader = (await import(chrome.runtime.getURL(`/gui/scripts/read.js`))).default;
const net = await import(chrome.runtime.getURL(`/scripts/net.js`));
let resource = false;
try {
resource = await net.download(source, `text`, true);
} catch (err) {}
if (resource) {
let metadata_element = document.createElement(`link`);
metadata_element.setAttribute(`rel`, `stylesheet`);
metadata_element.setAttribute(`type`, `text/css`);
metadata_element.setAttribute(`href`, source);
document.querySelector(`head`).appendChild(metadata_element);
} else {
throw new ReferenceError(reader.localized(`error_msg_fileNotFound`));
}
})();
} catch(err) {
(async() => {
const alerts = (await import(chrome.runtime.getURL(`/gui/scripts/alerts.js`))).default;
// Raise an alert.
alerts.error(err.name, err.message, err.stack, true, [source]);
// Stop loading the page when an error has occured; it's not going to work!
if (!DEBUG) {
window.close();
}
})();
// Stop loading immediately during the error.
break;
};
}
} }
// Get the window. // Prepare the window with its metadata.
this[`metadata`] = chrome.windows.getCurrent(); constructor() {
function headers() {
let LOAD_STATE = true;
let UI = {
CSS: [
chrome.runtime.getURL("gui/styles/external/fonts/materialdesignicons.min.css"),
chrome.runtime.getURL("gui/styles/external/materialize/css/materialize.css"),
chrome.runtime.getURL("gui/styles/ui.css"),
]
};
/* for (let index = 0; index < UI[`CSS`].length; index++) {
window_metadata[`id`] = window.id; const source = UI.CSS[index];
window_metadata[`focused`] = window.focused;
window_metadata[`state`] = window.state;
window_metadata[`type`] = window.type;
window_metadata[`incognito`] = window.incognito;
window_metadata[`alwaysOnTop`] = window.alwaysOnTop;
window_metadata[`sessionId`] = window.sessionId;
window_metadata[`tabs`] = window.tabs;*/
/* Fill in data and events. */ try {
function appearance() { (async () => {
// Add missing classes to all elements. // Import source reading for later.
function elements() { const reader = (await import(chrome.runtime.getURL(`/gui/scripts/read.js`))).default;
// Add buttons elements.
function buttons() { const net = await import(chrome.runtime.getURL(`/scripts/net.js`));
document.querySelectorAll(`button`).forEach((button) => {
if (!button.classList.contains(`btn`)) {
button.classList.add(`btn`);
}
});
[] let resource = false;
.concat( try {
document.querySelectorAll(`a`) resource = await net.download(source, `text`, true);
? document.querySelectorAll(`a`) } catch (err) {}
: [],
document.querySelectorAll(`button`)
? document.querySelectorAll(`button`)
: [],
document.querySelectorAll(
`input:not([type="checkbox"]):not([type="radio"]):not([type="range"])`,
)
? document.querySelectorAll(
`input:not([type="checkbox"]):not([type="radio"]):not([type="range"])`,
)
: [],
)
.forEach((ELEMENT_TYPE) => {
ELEMENT_TYPE.forEach((button) => {
if (
button.classList
? !button.classList.contains(`waves-effect`)
: true
) {
button.classList.add(`waves-effect`);
}
});
});
}
buttons();
}
function icons() { if (resource) {
let target_elements = document.querySelectorAll(`[data-icon]`); let metadata_element = document.createElement(`link`);
metadata_element.setAttribute(`rel`, `stylesheet`);
metadata_element.setAttribute(`type`, `text/css`);
metadata_element.setAttribute(`href`, source);
document.querySelector(`head`).appendChild(metadata_element);
} else {
throw new ReferenceError(reader.localized(`error_msg_fileNotFound`));
}
})();
} catch(err) {
(async() => {
const alerts = (await import(chrome.runtime.getURL(`/gui/scripts/alerts.js`))).default;
// Raise an alert.
alerts.error(err.name, err.message, err.stack, true, [source]);
target_elements.forEach((element) => { // Stop loading the page when an error has occured; it's not going to work!
// Get the content before removing it. if (!DEBUG) {
let element_data = {}; window.close();
}
})();
// Stop loading immediately during the error.
break;
};
}
}
// Swap the placement of the existing content. // Get the window.
function swap() { this[`metadata`] = chrome.windows.getCurrent();
element_data[`content`] = element.innerHTML;
element.innerHTML = ``;
let element_text = document.createElement(`span`); /*
element_text.innerHTML = element_data[`content`]; window_metadata[`id`] = window.id;
window_metadata[`focused`] = window.focused;
window_metadata[`state`] = window.state;
window_metadata[`type`] = window.type;
window_metadata[`incognito`] = window.incognito;
window_metadata[`alwaysOnTop`] = window.alwaysOnTop;
window_metadata[`sessionId`] = window.sessionId;
window_metadata[`tabs`] = window.tabs;*/
element.appendChild(element_text); /* Fill in data and events. */
} function appearance() {
// Add missing classes to all elements.
function elements() {
// Add buttons elements.
function buttons() {
document.querySelectorAll(`button`).forEach((button) => {
if (!button.classList.contains(`btn`)) {
button.classList.add(`btn`);
}
});
// Add the icon. []
function iconify() { .concat(
// Get the icon. document.querySelectorAll(`a`)
element_data[`icon`] = element.getAttribute(`data-icon`); ? document.querySelectorAll(`a`)
: [],
document.querySelectorAll(`button`)
? document.querySelectorAll(`button`)
: [],
document.querySelectorAll(
`input:not([type="checkbox"]):not([type="radio"]):not([type="range"])`,
)
? document.querySelectorAll(
`input:not([type="checkbox"]):not([type="radio"]):not([type="range"])`,
)
: [],
)
.forEach((ELEMENT_TYPE) => {
ELEMENT_TYPE.forEach((button) => {
if (
button.classList
? !button.classList.contains(`waves-effect`)
: true
) {
button.classList.add(`waves-effect`);
}
});
});
}
buttons();
}
// Get the icon. function icons() {
let icon_element = document.createElement(`i`); let target_elements = document.querySelectorAll(`[data-icon]`);
icon_element.className = `mdi mdi-`.concat(element_data[`icon`]);
element.prepend(icon_element);
}
swap(); target_elements.forEach((element) => {
iconify(); // Get the content before removing it.
}); let element_data = {};
}
function text() { // Swap the placement of the existing content.
let text_elements = {}; function swap() {
text_elements[`content`] = document.querySelectorAll("[for]"); element_data[`content`] = element.innerHTML;
text_elements[`alt`] = document.querySelectorAll("[alt-for]"); element.innerHTML = ``;
text_elements[`title`] = document.querySelectorAll("[title-for]");
text_elements[`content`].forEach((text_element) => { let element_text = document.createElement(`span`);
let text_inserted = texts.localized( element_text.innerHTML = element_data[`content`];
text_element.getAttribute(`for`),
false,
text_element.hasAttribute(`for-parameter`)
? text_element.getAttribute(`for-parameter`).split(",")
: null,
);
if (!text_inserted) {
text_inserted = texts.localized(
`term_`.concat(text_element.getAttribute(`for`)),
);
}
if (text_element.tagName.toLowerCase().includes(`input`)) { element.appendChild(element_text);
text_element.setAttribute(`placholder`, text_inserted); }
} else {
text_element.innerText = text_inserted;
}
});
delete text_elements[`content`]; // Add the icon.
Object.keys(text_elements).forEach((key) => { function iconify() {
if (text_elements[key]) { // Get the icon.
text_elements[key].forEach((text_element) => { element_data[`icon`] = element.getAttribute(`data-icon`);
let text_inserted = texts.localized(
text_element.getAttribute(key.concat(`-for`)),
false,
text_element.hasAttribute(key.concat(`for-parameter`))
? text_element
.getAttribute(key.concat(`for-parameter`))
.split(",")
: null,
);
if (!text_inserted) {
text_inserted = texts.localized(
`term_`.concat(text_element.getAttribute(key.concat(`-for`))),
);
}
text_element.setAttribute(key, text_inserted); // Get the icon.
}); let icon_element = document.createElement(`i`);
} icon_element.className = `mdi mdi-`.concat(element_data[`icon`]);
}); element.prepend(icon_element);
} }
async function storage() { swap();
// Import the module. iconify();
const secretariat = await import( });
chrome.runtime.getURL("scripts/secretariat.js") }
);
let input_elements = document.querySelectorAll("[data-store]"); function text() {
let text_elements = {};
text_elements[`content`] = document.querySelectorAll("[for]");
text_elements[`alt`] = document.querySelectorAll("[alt-for]");
text_elements[`title`] = document.querySelectorAll("[title-for]");
input_elements.forEach((input_element) => { text_elements[`content`].forEach((text_element) => {
// Gather data about the element. let text_inserted = texts.localized(
// Get the corresponding storage data. text_element.getAttribute(`for`),
let data = {}; false,
data[`source`] = input_element.getAttribute(`data-store`); text_element.hasAttribute(`for-parameter`)
data[`value`] = secretariat.read(data[`source`], -1); ? text_element.getAttribute(`for-parameter`).split(",")
: null,
);
if (!text_inserted) {
text_inserted = texts.localized(
`term_`.concat(text_element.getAttribute(`for`)),
);
}
data[`value`].then((value) => { if (text_element.tagName.toLowerCase().includes(`input`)) {
switch (input_element.getAttribute(`type`).toLowerCase()) { text_element.setAttribute(`placholder`, text_inserted);
case `checkbox`: } else {
input_element.checked = value; text_element.innerText = text_inserted;
break; }
case `progress`: });
case `range`:
// Ensure that it is a positive floating-point number.
value = !value ? 0 : Math.abs(parseFloat(value));
if (value > 100) {
value = value / 100;
}
// Set the attribute of the progress bar. delete text_elements[`content`];
input_element.setAttribute(`value`, value); Object.keys(text_elements).forEach((key) => {
input_element.setAttribute(`max`, 1); if (text_elements[key]) {
break; text_elements[key].forEach((text_element) => {
default: let text_inserted = texts.localized(
input_element.value = value ? value : ``; text_element.getAttribute(key.concat(`-for`)),
break; false,
} text_element.hasAttribute(key.concat(`for-parameter`))
}); ? text_element
}); .getAttribute(key.concat(`for-parameter`))
} .split(",")
: null,
);
if (!text_inserted) {
text_inserted = texts.localized(
`term_`.concat(text_element.getAttribute(key.concat(`-for`))),
);
}
elements(); text_element.setAttribute(key, text_inserted);
text(); });
icons(); }
storage(); });
} }
// Adds events to the window. async function storage() {
function events() { // Import the module.
/* Add events related to storage. */ const secretariat = await import(
async function storage() { chrome.runtime.getURL("scripts/secretariat.js")
// Import the module. );
const secretariat = await import(
chrome.runtime.getURL("scripts/secretariat.js")
);
let input_elements = document.querySelectorAll("[data-store]"); let input_elements = document.querySelectorAll("[data-store]");
input_elements.forEach((input_element) => { input_elements.forEach((input_element) => {
// Gather data about the element. // Gather data about the element.
// Get the corresponding storage data. // Get the corresponding storage data.
let data = {};
data[`source`] = input_element.getAttribute(`data-store`);
data[`value`] = secretariat.read(data[`source`], -1);
let element = {}; data[`value`].then((value) => {
element[`type`] = input_element.getAttribute(`type`).toLowerCase(); switch (input_element.getAttribute(`type`).toLowerCase()) {
element[`event`] = function () {}; case `checkbox`:
input_element.checked = value;
break;
case `progress`:
case `range`:
// Ensure that it is a positive floating-point number.
value = !value ? 0 : Math.abs(parseFloat(value));
if (value > 100) {
value = value / 100;
}
switch (element[`type`]) { // Set the attribute of the progress bar.
case `checkbox`: input_element.setAttribute(`value`, value);
element[`event`] = function () { input_element.setAttribute(`max`, 1);
let UI_item = {}; break;
UI_item[`source`] = this.getAttribute(`data-store`); default:
UI_item[`value`] = this.checked; input_element.value = value ? value : ``;
secretariat.write(UI_item[`source`], UI_item[`value`]); break;
}; }
break; });
default: });
element[`event`] = function () { }
let UI_item = {};
UI_item[`source`] = this.getAttribute(`data-store`);
UI_item[`value`] = element[`type`].includes(`num`)
? parseFloat(this.value) % 1 != 0
? parseFloat(this.value)
: parseInt(this.value)
: this.value;
secretariat.write(UI_item[`source`], UI_item[`value`]);
};
break;
}
input_element.addEventListener("change", element[`event`]); elements();
}); text();
} icons();
storage();
}
/* Map buttons to their corresponding action buttons. */ // Adds events to the window.
function actions() { function events() {
function links() { /* Add events related to storage. */
let buttons = document.querySelectorAll("button[href]"); async function storage() {
// Import the module.
const secretariat = await import(
chrome.runtime.getURL("scripts/secretariat.js")
);
if (buttons) { let input_elements = document.querySelectorAll("[data-store]");
buttons.forEach((button) => {
let event = function () {
// Get the data from the button.
let target = {};
target[`source`] = this.getAttribute(`href`);
// Get the correct path. input_elements.forEach((input_element) => {
target[`path`] = ( // Gather data about the element.
!target[`source`].includes(`://`) // Get the corresponding storage data.
? window.location.pathname
.split(`/`)
.slice(0, -1)
.join(`/`)
.concat(`/`)
: ``
).concat(target[`source`]);
windowman.new( let element = {};
target[`path`], element[`type`] = input_element.getAttribute(`type`).toLowerCase();
this.getAttribute(`tab-height`) element[`event`] = function () {};
? this.getAttribute(`tab-height`)
: null,
this.getAttribute(`tab-width`)
? this.getAttribute(`tab-width`)
: null,
);
};
button.addEventListener("click", event);
});
}
}
/* Enable the searching interface. */ switch (element[`type`]) {
function search() { case `checkbox`:
document.querySelectorAll(`[data-result]`).forEach((element) => { element[`event`] = function () {
// Begin searching when the textbox is changed. let UI_item = {};
element.addEventListener(`change`, async function () { UI_item[`source`] = this.getAttribute(`data-store`);
let search = {}; UI_item[`value`] = this.checked;
search[`criteria`] = element.value; secretariat.write(UI_item[`source`], UI_item[`value`]);
if (search[`criteria`]) { };
if ( break;
element.getAttribute(`data-results-filters`) default:
? element.getAttribute(`data-results-filters`).trim() element[`event`] = function () {
: false let UI_item = {};
) { UI_item[`source`] = this.getAttribute(`data-store`);
search[`additional criteria`] = element UI_item[`value`] = element[`type`].includes(`num`)
.getAttribute(`data-results-filters`) ? parseFloat(this.value) % 1 != 0
.split(`,`); ? parseFloat(this.value)
} : parseInt(this.value)
search[`source`] = element.getAttribute(`data-result`); : this.value;
search[`raw`] = await (async () => { secretariat.write(UI_item[`source`], UI_item[`value`]);
const secretariat = await import( };
chrome.runtime.getURL(`scripts/secretariat.js`) break;
); }
await secretariat.search( input_element.addEventListener("change", element[`event`]);
search[`source`], });
search[`criteria`], }
null,
search[`additional criteria`],
);
})();
}
});
});
}
// Responsiveness to different screen sizes. /* Map buttons to their corresponding action buttons. */
function resize() { function actions() {
function sidebar() { function links() {
if (document.querySelector(`.sidebar`)) { let buttons = document.querySelectorAll("button[href]");
if (window.innerWidth < 600) {
document
.querySelector(`.sidebar`)
.style.setProperty(`display`, `none`);
document
.querySelector(`.sidebar`)
.style.setProperty(`position`, `fixed`);
} else {
document
.querySelector(`.sidebar`)
.style.removeProperty(`display`);
document
.querySelector(`.sidebar`)
.style.removeProperty(`position`);
}
if (document.querySelector(`[data-action="ui,close,navbar"]`)) {
document
.querySelector(`[data-action="ui,close,navbar"]`)
.addEventListener(`click`, function () {
if (document.querySelector(`.sidebar`)) {
document
.querySelector(`.sidebar`)
.style.removeProperty("display");
document
.querySelector(`.sidebar`)
.style.removeProperty("position");
}
});
}
if (document.querySelector(`[data-action="ui,open,navbar"]`)) {
document
.querySelector(`[data-action="ui,open,navbar"]`)
.addEventListener(`click`, function () {
document
.querySelector(`.sidebar`)
.style.setProperty("display", "block");
document
.querySelector(`.sidebar`)
.style.setProperty("position", "fixed");
});
}
}
}
sidebar(); if (buttons) {
} buttons.forEach((button) => {
let event = function () {
// Get the data from the button.
let target = {};
target[`source`] = this.getAttribute(`href`);
window.addEventListener("resize", resize); // Get the correct path.
resize(); target[`path`] = (
search(); !target[`source`].includes(`://`)
links(); ? window.location.pathname
} .split(`/`)
.slice(0, -1)
.join(`/`)
.concat(`/`)
: ``
).concat(target[`source`]);
/* windowman.new(
Update the interface based on the storage data changes. target[`path`],
*/ this.getAttribute(`tab-height`)
async function updates() { ? this.getAttribute(`tab-height`)
// Import the module. : null,
const secretariat = await import( this.getAttribute(`tab-width`)
chrome.runtime.getURL("scripts/secretariat.js") ? this.getAttribute(`tab-width`)
); : null,
);
};
button.addEventListener("click", event);
});
}
}
// Get the storage data. /* Enable the searching interface. */
let storage_data = await secretariat.read(); function search() {
document.querySelectorAll(`[data-result]`).forEach((element) => {
// Begin searching when the textbox is changed.
element.addEventListener(`change`, async function () {
let search = {};
search[`criteria`] = element.value;
if (search[`criteria`]) {
if (
element.getAttribute(`data-results-filters`)
? element.getAttribute(`data-results-filters`).trim()
: false
) {
search[`additional criteria`] = element
.getAttribute(`data-results-filters`)
.split(`,`);
}
search[`source`] = element.getAttribute(`data-result`);
search[`raw`] = await (async () => {
const secretariat = await import(
chrome.runtime.getURL(`scripts/secretariat.js`)
);
async function enable() { await secretariat.search(
let input_elements = document.querySelectorAll("[data-enable]"); search[`source`],
search[`criteria`],
null,
search[`additional criteria`],
);
})();
}
});
});
}
if (input_elements) { // Responsiveness to different screen sizes.
input_elements.forEach((input_element) => { function resize() {
if (input_element.getAttribute("data-enable")) { function sidebar() {
(async () => {
input_element.disabled = !((await secretariat.read( if (document.querySelector(`.sidenav`)) {
input_element.getAttribute("data-enable"), (document.querySelectorAll(`.sidenav`)).forEach(function (sidebar_element) {
)) != null if (sidebar_element.getAttribute(`name`)) {
? (typeof (await secretariat.read( document.querySelector(`[works-sidebar="${sidebar_element.getAttribute(`name`)}"]`)
input_element.getAttribute("data-enable"), .addEventListener(`click`, function () {
))).includes(`obj`) M.Sidenav.getInstance(sidebar_element).open();
? ( });
await secretariat.read( } else if (document.querySelector(`[data-action="ui,open,navbar"]`)) {
input_element.getAttribute("data-enable"), document.querySelector(`[data-action="ui,open,navbar"]`).forEach(function (button_element) {
) button_element.addEventListener(`click`, function() {
).length > 0 M.Sidenav.getInstance(sidebar).open();
: !!(await secretariat.read( });
input_element.getAttribute("data-enable"), });
)) }
: false); });
})(); }
} }
});
}
}
// Update the input elements. sidebar();
secretariat.observe((what) => { }
enable();
});
enable(); resize();
} search();
links();
}
storage(); /*
actions(); Update the interface based on the storage data changes.
updates(); */
} async function updates() {
// Import the module.
const secretariat = await import(
chrome.runtime.getURL("scripts/secretariat.js")
);
headers(); // Get the storage data.
appearance(); let storage_data = await secretariat.read();
events();
} async function enable() {
let input_elements = document.querySelectorAll("[data-enable]");
if (input_elements) {
input_elements.forEach((input_element) => {
if (input_element.getAttribute("data-enable")) {
(async () => {
input_element.disabled = !((await secretariat.read(
input_element.getAttribute("data-enable"),
)) != null
? (typeof (await secretariat.read(
input_element.getAttribute("data-enable"),
))).includes(`obj`)
? (
await secretariat.read(
input_element.getAttribute("data-enable"),
)
).length > 0
: !!(await secretariat.read(
input_element.getAttribute("data-enable"),
))
: false);
})();
}
});
}
}
// Update the input elements.
secretariat.observe((what) => {
enable();
});
enable();
}
storage();
actions();
updates();
}
headers();
appearance();
events();
}
} }
export { windowman }; export { windowman };

View file

@ -24,7 +24,7 @@
--surface-color: #000000 !important; --surface-color: #000000 !important;
--font-color-main: rgba(255, 255, 255) !important; --font-color-main: rgba(255, 255, 255) !important;
--font-color-medium: rgba(255, 255, 255) !important; --font-color-medium: rgba(200, 200, 200) !important;
--font-color-disabled: rgba(255, 255, 255) !important; --font-color-disabled: rgba(255, 255, 255) !important;
--font-on-primary-color-main: rgba(255, 255, 255) !important; --font-on-primary-color-main: rgba(255, 255, 255) !important;
@ -164,42 +164,6 @@ nav .input-field label {
position: sticky; position: sticky;
z-index: 10; z-index: 10;
} }
@media screen and (min-width: 600px) {
.dual {
height: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dual .side {
width: 37%;
}
.dual > *:not(.side) {
flex-grow: 1;
}
}
@media screen and (min-width: 1000px) {
.dual > * {
transition: 0.1s ease-in-out;
}
.dual .side {
width: 25%;
}
}
.author { .author {
font-weight: italic; font-weight: italic;
} }
@media screen and (max-width: 600px) {
.sidebar {
display: none;
z-index: 20;
width: 100%;
}
}