Skip to content

Commit 7521df3

Browse files
committed
fix(web): hide empty navbar space on mobile
Signed-off-by: Arvind Marella <[email protected]>
1 parent df1a2ac commit 7521df3

File tree

3 files changed

+155
-12
lines changed

3 files changed

+155
-12
lines changed

src/generators/web/ui/index.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,27 @@ main {
7171
}
7272
}
7373

74+
/* Navigation Bar */
75+
nav [class*='navItems']:empty {
76+
display: none !important;
77+
}
78+
79+
@media (min-width: 64rem) {
80+
nav [class*='actionsWrapper'] {
81+
margin-left: auto;
82+
}
83+
}
84+
7485
#modalContent li > div > a {
7586
max-width: 42rem;
87+
7688
> svg {
7789
flex-shrink: 0;
7890
}
91+
7992
> div {
8093
min-width: 0;
94+
8195
> p {
8296
overflow: hidden;
8397
text-overflow: ellipsis;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use strict';
2+
3+
import assert from 'node:assert/strict';
4+
import { describe, it, mock } from 'node:test';
5+
6+
let readFileCalls = 0;
7+
let bundleAsyncCalls = 0;
8+
9+
mock.module('node:fs/promises', {
10+
namedExports: {
11+
readFile: async () => {
12+
readFileCalls += 1;
13+
return 'body { color: red; }';
14+
},
15+
},
16+
});
17+
18+
mock.module('lightningcss', {
19+
namedExports: {
20+
bundleAsync: async ({ cssModules }) => {
21+
bundleAsyncCalls += 1;
22+
23+
return {
24+
code: Buffer.from(cssModules ? '.module{}' : '.global{}'),
25+
exports: cssModules ? { foo: { name: '_foo_hash' } } : {},
26+
};
27+
},
28+
},
29+
});
30+
31+
const createCssLoader = (await import('../css.mjs')).default;
32+
33+
describe('css loader', () => {
34+
it('returns empty JS for global CSS but still emits collected CSS', async () => {
35+
readFileCalls = 0;
36+
bundleAsyncCalls = 0;
37+
38+
const plugin = createCssLoader();
39+
40+
const result = await plugin.load.handler('C:/tmp/styles.css');
41+
assert.deepStrictEqual(result, {
42+
code: '',
43+
moduleType: 'js',
44+
moduleSideEffects: 'no-treeshake',
45+
});
46+
47+
let emitted;
48+
plugin.buildEnd.call({
49+
emitFile(file) {
50+
emitted = file;
51+
return 'ref';
52+
},
53+
});
54+
55+
assert.deepStrictEqual(emitted, {
56+
type: 'asset',
57+
name: 'styles.css',
58+
source: '.global{}',
59+
});
60+
61+
assert.equal(readFileCalls, 1);
62+
assert.equal(bundleAsyncCalls, 1);
63+
});
64+
65+
it('exports class mapping for CSS modules and caches results', async () => {
66+
readFileCalls = 0;
67+
bundleAsyncCalls = 0;
68+
69+
const plugin = createCssLoader();
70+
71+
const first = await plugin.load.handler('C:/tmp/index.module.css');
72+
const second = await plugin.load.handler('C:/tmp/index.module.css');
73+
74+
assert.deepStrictEqual(first, {
75+
code: 'export default {"foo":"_foo_hash"};',
76+
moduleType: 'js',
77+
moduleSideEffects: 'no-treeshake',
78+
});
79+
assert.deepStrictEqual(second, first);
80+
81+
let emitted;
82+
plugin.buildEnd.call({
83+
emitFile(file) {
84+
emitted = file;
85+
return 'ref';
86+
},
87+
});
88+
89+
assert.deepStrictEqual(emitted, {
90+
type: 'asset',
91+
name: 'styles.css',
92+
source: '.module{}',
93+
});
94+
95+
assert.equal(readFileCalls, 1);
96+
assert.equal(bundleAsyncCalls, 1);
97+
});
98+
});

src/generators/web/utils/css.mjs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { readFile } from 'node:fs/promises';
2+
import { createRequire } from 'node:module';
3+
import { resolve, dirname } from 'node:path';
24

35
import { bundleAsync } from 'lightningcss';
46

7+
const requireFn = createRequire(import.meta.url);
8+
59
// Since we use rolldown to bundle multiple times,
610
// we re-use a lot of CSS files, so there is no
711
// need to re-transpile.
@@ -26,19 +30,21 @@ export default () => {
2630

2731
// Hook into the module loading phase of Rolldown
2832
load: {
29-
// Match only files ending with `.module.css`
33+
// Match only files ending with `.css`
3034
filter: {
3135
id: {
32-
include: /\.module\.css$/,
36+
include: /\.css$/,
3337
},
3438
},
3539

3640
/**
37-
* Load handler to process matched `.module.css` files
41+
* Load handler to process matched `.css` files
3842
*
3943
* @param {string} id - Absolute file path to the CSS file
4044
*/
4145
async handler(id) {
46+
const isModule = /\.module\.css$/.test(id);
47+
4248
// Return from cache if already processed
4349
if (fileCache.has(id)) {
4450
const cached = fileCache.get(id);
@@ -47,39 +53,64 @@ export default () => {
4753
cssChunks.add(cached.code);
4854

4955
return {
50-
code: `export default ${JSON.stringify(cached.exports)};`,
56+
code: isModule
57+
? `export default ${JSON.stringify(cached.exports)};`
58+
: '',
5159
moduleType: 'js',
60+
moduleSideEffects: 'no-treeshake',
5261
};
5362
}
5463

5564
// Read the raw CSS file from disk
5665
const source = await readFile(id, 'utf8');
5766

58-
// Use Lightning CSS to compile the file with CSS Modules enabled
67+
// Use Lightning CSS to compile the file
5968
const { code, exports } = await bundleAsync({
6069
filename: id,
6170
code: Buffer.from(source),
62-
cssModules: true,
71+
cssModules: isModule,
72+
resolver: {
73+
/**
74+
* @param {string} specifier
75+
* @param {string} from
76+
*/
77+
resolve(specifier, from) {
78+
if (specifier.startsWith('./') || specifier.startsWith('../')) {
79+
return resolve(dirname(from), specifier);
80+
}
81+
82+
try {
83+
return requireFn.resolve(specifier, { paths: [dirname(from)] });
84+
} catch {
85+
return specifier;
86+
}
87+
},
88+
},
6389
});
6490

6591
const css = code.toString();
6692

6793
// Add the compiled CSS to our in-memory collection
6894
cssChunks.add(css);
6995

70-
// Map exported class names to their scoped identifiers
96+
// Map exported class names to their scoped identifiers if it's a module
7197
// e.g., { button: '_button_abc123' }
72-
const mappedExports = Object.fromEntries(
73-
Object.entries(exports).map(([key, value]) => [key, value.name])
74-
);
98+
const mappedExports = isModule
99+
? Object.fromEntries(
100+
Object.entries(exports).map(([key, value]) => [key, value.name])
101+
)
102+
: {};
75103

76104
// Cache result
77105
fileCache.set(id, { code: css, exports: mappedExports });
78106

79-
// Return a JS module that exports the scoped class names
107+
// Return a JS module that exports the scoped class names (or nothing for global CSS)
80108
return {
81-
code: `export default ${JSON.stringify(mappedExports)};`,
109+
code: isModule
110+
? `export default ${JSON.stringify(mappedExports)};`
111+
: '',
82112
moduleType: 'js',
113+
moduleSideEffects: 'no-treeshake',
83114
};
84115
},
85116
},

0 commit comments

Comments
 (0)