1
0
mirror of https://github.com/jdan/98.css.git synced 2026-05-05 06:01:22 +09:00

feature: add tabs

This commit is contained in:
Juan Garay
2023-03-09 22:17:43 -03:00
9 changed files with 6471 additions and 957 deletions

23
.github/workflows/npm-publish.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
name: Publish to NPM
on:
workflow_dispatch:
release:
types: [published]
jobs:
publish-npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm run release
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

View File

@@ -9,7 +9,7 @@ A design system for building faithful recreations of old UIs.
98.css is a CSS file that takes semantic HTML and makes it look pretty. It does not ship with any JavaScript, so it is compatible with your frontend framework of choice.
Be sure to check out [XP.css](https://botoxparty.github.io/XP.css/) as well.
Be sure to check out [XP.css](https://botoxparty.github.io/XP.css/) and [7.css](https://khang-nd.github.io/7.css/) as well.
### Installation / Usage

View File

@@ -102,10 +102,6 @@ summary {
display: inline;
}
summary::-webkit-details-marker {
display: none;
}
details[open] summary {
margin-bottom: 8px;
}

View File

@@ -37,9 +37,12 @@
<ul>
<li><a href="#title-bar">Title Bar</a></li>
<li><a href="#window-contents">Window contents</a></li>
<li><a href="#status-bar">Status Bar</a></li>
</ul>
</li>
<li><a href="#tree-view">TreeView</a></li>
<li><a href="#tabs">Tabs</a></li>
<li><a href="#table-view">TableView</a></li>
</ul>
</li>
<li><a href="#issues-contributing-etc">Issues, Contributing, etc.</a></li>
@@ -137,7 +140,9 @@
They are given 12px of horizontal padding by default.
</p>
<%- example(`<button>Click me</button>`) %>
<%- example(`<button>Click me</button>
<input type="submit" />
<input type="reset" /> `)%>
<p>
When buttons are clicked, the raised borders become sunken.
@@ -388,7 +393,40 @@
<textarea id="text${getCurrentId()}" rows="8"></textarea>
</div>
`) %>
<p>
Text boxes can also be disabled and have value with their corresponding HTML attributes.
</p>
<%- example(`
<div class="field-row">
<label for="text${getNewId()}">Favorite color</label>
<input id="text${getCurrentId()}" disabled type="text" value="Windows Green"/>
</div>
`) %>
<p>
Other types of HTML5 text inputs are also supported.
</p>
<%- example(`
<div class="field-row-stacked" style="width: 200px">
<label for="text${getNewId()}">Email</label>
<input id="text${getCurrentId()}" type="email" value="admin@contoso.com"/>
</div>
<div class="field-row-stacked" style="width: 200px">
<label for="text${getNewId()}">Password</label>
<input id="text${getCurrentId()}" type="password" value="hunter2"/>
</div>
<div class="field-row-stacked" style="width: 200px">
<label for="text${getNewId()}">Favorite Number</label>
<input id="text${getCurrentId()}" type="number" value="98"/>
</div>
`) %>
</div>
</section>
<section class="component">
@@ -635,6 +673,48 @@
</div>
</section>
<section class="component">
<h4 id="status-bar">Status Bar</h4>
<div>
<blockquote>
A status bar is a special area within a window, typically the bottom, that displays information
about the current state of what is being viewed in the window or any other contextual information, such as keyboard
state.
<footer>
&mdash; Microsoft Windows User Experience p. 146
</footer>
</blockquote>
<p>
You can render a status bar with the <code>status-bar</code> class,
and <code>status-bar-field</code> for every child text element.
</p>
<%- example(`
<div class="window" style="width: 320px">
<div class="title-bar">
<div class="title-bar-text">A Window With A Status Bar</div>
</div>
<div class="window-body">
<p> There are just so many possibilities:</p>
<ul>
<li>A Task Manager</li>
<li>A Notepad</li>
<li>Or even a File Explorer!</li>
</ul>
</div>
<div class="status-bar">
<p class="status-bar-field">Press F1 for help</p>
<p class="status-bar-field">Slide 1</p>
<p class="status-bar-field">CPU Usage: 14%</p>
</div>
</div>
`) %>
</section>
<section class="component">
<h3 id="tree-view">TreeView</h3>
<div>
@@ -724,10 +804,11 @@
<h3 id="tabs">Tabs</h3>
<div>
<blockquote>
...
A <em>tab control</em> is analogous to a divider in a file cabinet or notebook.
You can use this control to define multiple logical pages or sections of information within the same window.
<footer>
&mdash; Microsoft Windows User Experience p. #
&mdash; Microsoft Windows User Experience p. 193
</footer>
</blockquote>
@@ -747,12 +828,12 @@
<p>Hello, world!</p>
<menu role="tablist">
<li role="tab" aria-selected="true"><a href="#">Desktop</a></li>
<li role="tab"><a href="#">My computer</a></li>
<li role="tab"><a href="#">Control panel</a></li>
<li role="tab"><a href="#">Devices manager</a></li>
<li role="tab"><a href="#">Hardware profiles</a></li>
<li role="tab"><a href="#">Performance</a></li>
<li role="tab" aria-selected="true"><a href="#tabs">Desktop</a></li>
<li role="tab"><a href="#tabs">My computer</a></li>
<li role="tab"><a href="#tabs">Control panel</a></li>
<li role="tab"><a href="#tabs">Devices manager</a></li>
<li role="tab"><a href="#tabs">Hardware profiles</a></li>
<li role="tab"><a href="#tabs">Performance</a></li>
</menu>
<div class="window" role="tabpanel">
<div class="window-body">
@@ -772,20 +853,20 @@
<p>Hello, world!</p>
<menu role="tablist" class="multirows">
<li role="tab"><a href="#">Desktop</a></li>
<li role="tab"><a href="#">My computer</a></li>
<li role="tab"><a href="#">Control panel</a></li>
<li role="tab"><a href="#">Devices manager</a></li>
<li role="tab"><a href="#">Hardware profiles</a></li>
<li role="tab"><a href="#">Performance</a></li>
<li role="tab"><a href="#tabs">Desktop</a></li>
<li role="tab"><a href="#tabs">My computer</a></li>
<li role="tab"><a href="#tabs">Control panel</a></li>
<li role="tab"><a href="#tabs">Devices manager</a></li>
<li role="tab"><a href="#tabs">Hardware profiles</a></li>
<li role="tab"><a href="#tabs">Performance</a></li>
</menu>
<menu role="tablist" class="multirows">
<li role="tab"><a href="#">Users</a></li>
<li role="tab"><a href="#">Network</a></li>
<li role="tab"><a href="#">Programs</a></li>
<li role="tab"><a href="#">Services</a></li>
<li role="tab"><a href="#">Resources</a></li>
<li role="tab"><a href="#">Advanced</a></li>
<li role="tab"><a href="#tabs">Users</a></li>
<li role="tab"><a href="#tabs">Network</a></li>
<li role="tab"><a href="#tabs">Programs</a></li>
<li role="tab"><a href="#tabs">Services</a></li>
<li role="tab"><a href="#tabs">Resources</a></li>
<li role="tab"><a href="#tabs">Advanced</a></li>
</menu>
<div class="window" role="tabpanel">
<div class="window-body">
@@ -794,6 +875,96 @@
</div>
</div>
`) %>
</div>
</section>
<section class="component">
<h3 id="table-view">TableView</h3>
<div>
<p>
To render a table view, use a table element. Wrap it with a div element with <code>sunken-panel</code> class to provide proper border and overflow container.
</p>
<p>
With a bit of extra scripting you can make table view interactive. Give <code>interactive</code> class to
table element to show pointer cursor when hovering over body rows. Table rows can be given
<code>highlighted</code> class to appear selected.
</p>
<%- example(`
<div class="sunken-panel" style="height: 120px; width: 240px;">
<table class="interactive">
<thead>
<tr>
<th>Name</th>
<th>Version</th>
<th>Company</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL ODBC 3.51 Driver</td>
<td>3.51.11.00</td>
<td>MySQL AB</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
<tr>
<td>SQL Server</td>
<td>3.70.06.23</td>
<td>Microsoft Corporation</td>
</tr>
</tbody>
</table>
</div>
<script>
document.querySelectorAll('table.interactive').forEach(element => {
element.addEventListener('click', (event) => {
const row = event.path.find(element => element.tagName === 'TR' && element.parentElement.tagName === 'TBODY');
if (row) {
row.classList.toggle('highlighted');
}
})
});
</script>
`) %>
</div>
</section>

4
icon/groupbox-border.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg width="5" height="5" viewBox="0 0 5 5" fill="grey" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H5V5H0V2H2V3H3V2H0" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H4V4H0V1H1V3H3V1H0" fill="#808080" />
</svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -0,0 +1,10 @@
<svg width="5" height="5" viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
<rect width="4" height="1" x="0" y="0" fill="#808080"/>
<rect width="1" height="4" x="0" y="0" fill="#808080"/>
<rect width="2" height="1" x="1" y="1" fill="#0a0a0a"/>
<rect width="1" height="2" x="1" y="1" fill="#0a0a0a"/>
<rect width="5" height="1" x="0" y="4" fill="#fff"/>
<rect width="1" height="5" x="4" y="0" fill="#fff"/>
<rect width="1" height="3" x="3" y="1" fill="#dfdfdf"/>
<rect width="3" height="1" x="1" y="3" fill="#dfdfdf"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

7039
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "98.css",
"version": "0.1.16",
"version": "0.1.18",
"description": "A design system for building faithful recreations of old UIs",
"main": "dist/98.css",
"directories": {
@@ -27,19 +27,19 @@
"homepage": "https://github.com/jdan/98.css",
"devDependencies": {
"chokidar": "^3.3.1",
"cssnano": "^4.1.10",
"cssnano": "^5.0.1",
"dedent": "^0.7.0",
"ejs": "^3.0.2",
"gh-pages": "^2.2.0",
"glob": "^7.1.6",
"highlight.js": "^9.18.1",
"highlight.js": "^10.4.1",
"live-server": "^1.2.1",
"mkdirp": "^1.0.4",
"postcss": "^8.2.12",
"postcss-calc": "^7.0.2",
"postcss-copy": "^7.1.0",
"postcss-css-variables": "^0.14.0",
"postcss-inline": "^1.2.0",
"postcss-inline-svg": "^4.1.0"
},
"dependencies": {}
}
}

123
style.css
View File

@@ -6,6 +6,7 @@
:root {
/* Color */
--text-color: #222222;
--surface: #c0c0c0;
--button-highlight: #ffffff;
--button-face: #dfdfdf;
@@ -95,7 +96,7 @@
body {
font-family: Arial;
font-size: 12px;
color: #222222;
color: var(--text-color);
}
button,
@@ -104,6 +105,7 @@ input,
textarea,
select,
option,
table,
ul.tree-view,
.window,
.title-bar {
@@ -133,9 +135,13 @@ u {
border-bottom: 0.5px solid #222222;
}
button {
button,
input[type="submit"],
input[type="reset"] {
box-sizing: border-box;
border: none;
color: transparent;
text-shadow: 0 0 var(--text-color);
background: var(--surface);
box-shadow: var(--border-raised-outer), var(--border-raised-inner);
border-radius: 0;
@@ -152,29 +158,45 @@ button {
box-shadow: var(--border-raised-outer), var(--border-raised-inner);
}
button:not(:disabled):active {
button:not(:disabled):active,
input[type="submit"]:not(:disabled):active,
input[type="reset"]:not(:disabled):active {
box-shadow: var(--border-sunken-outer), var(--border-sunken-inner);
padding: 2px 11px 0 13px;
text-shadow: 1px 1px var(--text-color);
}
@media (not(hover)) {
button:not(:disabled):hover {
button:not(:disabled):hover,
input[type="submit"]:not(:disabled):hover,
input[type="reset"]:not(:disabled):hover {
box-shadow: var(--border-sunken-outer), var(--border-sunken-inner);
}
}
button:focus {
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus {
outline: 1px dotted #000000;
outline-offset: -4px;
}
button::-moz-focus-inner {
button::-moz-focus-inner,
input[type="submit"]::-moz-focus-inner,
input[type="reset"]::-moz-focus-inner {
border: 0;
}
:disabled,
:disabled + label {
:disabled + label,
input[readonly],
input[readonly] + label {
color: var(--button-shadow);
}
button:disabled,
input[type="submit"]:disabled,
input[type="reset"]:disabled,
:disabled + label {
text-shadow: 1px 1px 0 var(--button-highlight);
}
@@ -261,14 +283,25 @@ button::-moz-focus-inner {
background-position: top 3px left 4px;
}
.status-bar {
margin: 0px 1px;
display: flex;
gap: 1px;
}
.status-bar-field {
box-shadow: inset -1px -1px #dfdfdf, inset 1px 1px #808080;
flex-grow: 1;
padding: 2px 3px;
margin: 0;
}
.window-body {
margin: var(--element-spacing);
}
fieldset {
border: none;
box-shadow: inset -1px -1px var(--button-highlight), inset -2px 1px var(--button-shadow),
inset 1px -2px var(--button-shadow), inset 2px 2px var(--button-highlight);
border-image: svg-load("./icon/groupbox-border.svg") 2;
padding: calc(2 * var(--border-width) + var(--element-spacing));
padding-block-start: var(--element-spacing);
margin: 0;
@@ -412,6 +445,7 @@ input[type="checkbox"][disabled]:checked + label::after {
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
select,
textarea {
padding: 3px 4px;
@@ -431,14 +465,31 @@ input[type="email"],
select {
height: 21px;
}
input[type="number"] {
/* need this 1 pixel to fit the spinner controls in box */
height: 22px;
}
input[type="text"],
input[type="password"],
input[type="email"] {
input[type="email"],
input[type="number"] {
/* For some reason descenders are getting cut off without this */
line-height: 2;
}
input[type="email"]:disabled,
input[type="password"]:disabled,
input[type="text"]:disabled,
input[type="number"]:disabled,
input[type="email"]:read-only,
input[type="password"]:read-only,
input[type="text"]:read-only,
input[type="number"]:read-only,
textarea:disabled {
background-color: var(--surface);
}
select {
appearance: none;
-webkit-appearance: none;
@@ -455,6 +506,7 @@ select:focus,
input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
input[type="number"]:focus,
textarea:focus {
outline: none;
}
@@ -475,6 +527,8 @@ input[type="range"]::-webkit-slider-thumb {
width: 11px;
background: svg-load("./icon/indicator-horizontal.svg");
transform: translateY(-8px);
box-shadow: none;
border: none;
}
input[type="range"].has-box-indicator::-webkit-slider-thumb {
@@ -678,6 +732,11 @@ ul.tree-view details[open] > summary:before {
content: "-";
}
ul.tree-view details > summary::marker,
ul.tree-view details > summary::-webkit-details-marker {
content: "";
}
pre {
display: block;
background: var(--button-highlight);
@@ -786,3 +845,43 @@ menu[role=tablist].multirows > li {
flex-grow: 1;
text-align: center;
}
.sunken-panel {
box-sizing: border-box;
border: 2px groove transparent;
border-image: svg-load("./icon/sunken-panel-border.svg") 2;
overflow: auto;
background-color: #fff;
}
table {
border-collapse: collapse;
position: relative;
text-align: left;
white-space: nowrap;
background-color: #fff;
}
table > thead > tr > * {
position: sticky;
top: 0;
height: 17px;
box-shadow: var(--border-raised-outer), var(--border-raised-inner);
background: var(--surface);
box-sizing: border-box;
font-weight: normal;
padding: 0 var(--grouped-element-spacing);
}
table.interactive > tbody > tr {
cursor: pointer;
}
table > tbody > tr.highlighted {
color: #fff;
background-color: var(--dialog-blue);
}
table > tbody > tr > * {
padding: 0 var(--grouped-element-spacing);
height: 14px;
}