Implement a minimal, dependency-free publishing tool to manage news and articles. The app includes: - A Markdown-based block editor with rich-text capabilities. - Image upload and resizing using GD. - Support for drafts, scheduled publishing, and a trash system. - Integrated Basic Auth protection via PHP and .htpasswd. - A global plugin for automatic execution of scheduled posts. The tool reuses core site logic in custom/plugins/publiser-lib.php to ensure consistency between the editor and the live site.
376 lines
8.4 KiB
CSS
376 lines
8.4 KiB
CSS
/* /publiser admin app - reuses the site's CSS variables (base.css) for
|
|
brand consistency, but is otherwise a self-contained layout. Follows the
|
|
site convention of margin-top-only spacing (global reset zeroes
|
|
margin-bottom). */
|
|
|
|
/* Reuses the site's own .button class for nav/action links (base.css), so
|
|
they inherit real site styling instead of parallel custom classes. The
|
|
header bar itself has no site equivalent (the public site's header is a
|
|
narrower, logo-anchored layout) - .p-topbar-inner matches .p-main's width
|
|
instead, left-aligned. */
|
|
|
|
.p-app {
|
|
font-family: var(--font-body);
|
|
color: var(--color-grey);
|
|
background: var(--color-green-light);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.p-topbar {
|
|
background: #fff;
|
|
border-bottom: 3px #00000022 solid;
|
|
}
|
|
|
|
.p-topbar-inner {
|
|
max-width: 64rem;
|
|
margin: 0 auto;
|
|
padding: .8rem 1rem;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.p-topbar-inner ul {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
list-style: none;
|
|
gap: .6rem;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.p-main {
|
|
max-width: 64rem;
|
|
margin: 0 auto;
|
|
padding: 1.5rem 1rem 4rem;
|
|
}
|
|
|
|
h1 {
|
|
font-family: var(--font-heading);
|
|
color: var(--color-green);
|
|
font-size: 1.8rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.p-muted { color: #777; font-size: .9rem }
|
|
|
|
.p-editor-topline {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.p-back, .p-view-live {
|
|
display: inline-block;
|
|
margin-top: 0;
|
|
font-size: .9rem;
|
|
}
|
|
|
|
.p-notice {
|
|
margin-top: .8rem;
|
|
padding: .6rem .9rem;
|
|
background: var(--color-green-light);
|
|
border: 1px solid var(--color-green);
|
|
border-radius: .4rem;
|
|
color: var(--color-grey);
|
|
}
|
|
.p-notice-error {
|
|
background: #fdecea;
|
|
border-color: #c0392b;
|
|
}
|
|
|
|
/* --- list view --- */
|
|
|
|
.p-header-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: baseline;
|
|
gap: 1rem;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.p-section-switch a {
|
|
padding: .3rem .7rem;
|
|
border-radius: .4rem;
|
|
font-size: .9rem;
|
|
}
|
|
.p-section-switch a.active {
|
|
background: var(--color-green);
|
|
color: #fff !important;
|
|
}
|
|
|
|
.p-empty { margin-top: 2rem; color: #777 }
|
|
|
|
.p-list {
|
|
list-style: none;
|
|
margin-top: 1rem;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .6rem;
|
|
}
|
|
|
|
.p-list-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
background: #fff;
|
|
border-radius: .5rem;
|
|
padding: .6rem .9rem;
|
|
border: 1px solid #00000012;
|
|
}
|
|
|
|
.p-list-cover {
|
|
width: 4rem;
|
|
height: 4rem;
|
|
object-fit: cover;
|
|
border-radius: .4rem;
|
|
flex: none;
|
|
}
|
|
.p-list-cover-empty { background: #eee }
|
|
|
|
.p-list-info { flex: 1; min-width: 0 }
|
|
.p-list-title {
|
|
font-weight: 700;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.p-list-meta { font-size: .85rem; color: #777; margin-top: .1rem }
|
|
|
|
.p-list-actions form { margin: 0 }
|
|
|
|
/* --- buttons & forms --- */
|
|
|
|
/* base.css's .button has no destructive/danger variant - the one addition
|
|
needed on top of the site's real button styling. */
|
|
.button.danger { outline-color: #c0392b; color: #c0392b }
|
|
.button.danger:hover, .button.danger:focus { background-color: #c0392b; color: #fff; outline: none }
|
|
|
|
.p-form label {
|
|
display: block;
|
|
margin-top: 1rem;
|
|
font-size: .82rem;
|
|
font-weight: 600;
|
|
color: #555;
|
|
}
|
|
.p-side-box .p-form label:first-of-type,
|
|
.p-side-box label:first-of-type { margin-top: .6rem }
|
|
|
|
.p-form input[type=text],
|
|
.p-form input[type=date],
|
|
.p-form input[type=datetime-local],
|
|
.p-form textarea,
|
|
.p-form select {
|
|
display: block;
|
|
width: 100%;
|
|
margin-top: .35rem;
|
|
padding: .5rem .6rem;
|
|
border: 1px solid #00000022;
|
|
border-radius: .4rem;
|
|
font-family: var(--font-body);
|
|
font-size: .95rem;
|
|
color: var(--color-grey);
|
|
background: #fbfbfa;
|
|
box-sizing: border-box;
|
|
transition: border-color .15s, background .15s;
|
|
}
|
|
.p-form input[type=text]:focus,
|
|
.p-form input[type=date]:focus,
|
|
.p-form input[type=datetime-local]:focus,
|
|
.p-form textarea:focus,
|
|
.p-form select:focus {
|
|
outline: none;
|
|
border-color: var(--color-green);
|
|
background: #fff;
|
|
}
|
|
.p-form textarea { resize: vertical }
|
|
/* .p-form textarea's `display: block` above outranks the browser's native
|
|
[hidden] { display: none }, which un-hides this fallback field (it holds
|
|
the untouched block's raw content and must never actually show). Force it
|
|
back off with higher specificity. */
|
|
.p-form textarea.p-block-hidden { display: none }
|
|
|
|
.p-form-new { max-width: 28rem }
|
|
.p-form-new button { margin-top: 1.5rem }
|
|
|
|
/* --- editor --- */
|
|
|
|
.p-editor-grid {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) 18rem;
|
|
gap: 1.5rem;
|
|
margin-top: 1rem;
|
|
align-items: start;
|
|
}
|
|
|
|
@media (max-width: 55rem) {
|
|
.p-editor-grid { grid-template-columns: 1fr }
|
|
}
|
|
|
|
.p-title-input {
|
|
width: 100%;
|
|
font-family: var(--font-heading);
|
|
font-size: 1.8rem;
|
|
border: none;
|
|
border-bottom: 2px solid #00000015;
|
|
padding: .3rem 0;
|
|
background: transparent;
|
|
box-sizing: border-box;
|
|
}
|
|
.p-title-input:focus { outline: none; border-bottom-color: var(--color-green) }
|
|
|
|
.p-blocks {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.p-block {
|
|
background: #fff;
|
|
border: 1px solid #00000015;
|
|
border-radius: .5rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.p-block-toolbar {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: .2rem;
|
|
padding: .4rem;
|
|
background: #fafaf8;
|
|
border-bottom: 1px solid #00000012;
|
|
}
|
|
|
|
.p-block-toolbar button {
|
|
border: 1px solid transparent;
|
|
background: transparent;
|
|
padding: .3rem .55rem;
|
|
border-radius: .3rem;
|
|
cursor: pointer;
|
|
font-size: .9rem;
|
|
color: var(--color-grey);
|
|
font-family: var(--font-body);
|
|
}
|
|
.p-block-toolbar button:hover { background: #eee }
|
|
.p-block-toolbar button.danger { color: #c0392b }
|
|
.p-block-toolbar button.danger:hover { background: #fdecea }
|
|
|
|
.p-block-spacer { flex: 1 }
|
|
.p-block-label { font-size: .85rem; color: #777; padding: 0 .4rem }
|
|
|
|
.p-richtext {
|
|
min-height: 6rem;
|
|
padding: 1rem;
|
|
line-height: 1.5;
|
|
}
|
|
.p-richtext:focus { outline: none; background: #fffef8 }
|
|
.p-richtext h2, .p-richtext h3 { margin-top: .6em; color: var(--color-green) }
|
|
.p-richtext p { margin-top: .8em }
|
|
.p-richtext p:first-child { margin-top: 0 }
|
|
.p-richtext img { max-width: 100%; border-radius: .3rem; margin-top: .5em }
|
|
.p-richtext blockquote {
|
|
margin-top: .8em;
|
|
padding-left: 1rem;
|
|
border-left: 3px solid var(--color-green);
|
|
color: #555;
|
|
}
|
|
.p-richtext ul, .p-richtext ol { margin-top: .5em; padding-left: 1.4rem }
|
|
|
|
.p-raw-html {
|
|
margin-top: .8em;
|
|
padding: .6rem;
|
|
border: 1px dashed #00000030;
|
|
border-radius: .4rem;
|
|
background: #fafaf8;
|
|
cursor: default;
|
|
position: relative;
|
|
}
|
|
.p-raw-html::before {
|
|
content: "Innebygd kode - kan flyttes/slettes, men ikke redigeres her";
|
|
display: block;
|
|
margin-bottom: .5em;
|
|
font-size: .75rem;
|
|
color: #888;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
}
|
|
|
|
.p-code {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
border: none;
|
|
padding: 1rem;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: .85rem;
|
|
resize: vertical;
|
|
}
|
|
.p-code:focus { outline: none; background: #fffef8 }
|
|
|
|
.p-add-block {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .6rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.p-extra-files {
|
|
margin-top: 1.5rem;
|
|
padding: .8rem;
|
|
background: #fafaf8;
|
|
border-radius: .4rem;
|
|
font-size: .9rem;
|
|
}
|
|
.p-extra-files ul { margin-top: .3rem; padding-left: 1.2rem }
|
|
|
|
.p-editor-sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.p-side-box {
|
|
background: #fff;
|
|
border: 1px solid #00000015;
|
|
border-radius: .6rem;
|
|
padding: 1.1rem;
|
|
box-shadow: 0 1px 2px #00000008;
|
|
}
|
|
|
|
.p-side-label {
|
|
font-family: var(--font-heading);
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
color: var(--color-green);
|
|
padding-bottom: .5rem;
|
|
border-bottom: 1px solid #00000012;
|
|
}
|
|
|
|
.p-actions-box { display: flex; flex-direction: column; gap: .6rem }
|
|
.p-actions-box .button { width: 100%; text-align: center; box-sizing: border-box }
|
|
.p-actions-box label { margin-top: .8rem }
|
|
.p-actions-box .p-muted { margin-top: 0 }
|
|
|
|
.p-cover-drop {
|
|
margin-top: .5rem;
|
|
border: 2px dashed #00000030;
|
|
border-radius: .4rem;
|
|
min-height: 6rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
font-size: .85rem;
|
|
color: #777;
|
|
cursor: pointer;
|
|
padding: .5rem;
|
|
overflow: hidden;
|
|
}
|
|
.p-cover-drop.dragover { border-color: var(--color-green); background: var(--color-green-light) }
|
|
.p-cover-drop img { width: 100%; height: 8rem; object-fit: cover; border-radius: .3rem }
|
|
|
|
@media (max-width: 30rem) {
|
|
.p-main { padding: 1rem .7rem 4rem }
|
|
.p-list-item { flex-wrap: wrap }
|
|
}
|