@dirsigler/astro-techradar 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +257 -0
- package/config.ts +95 -0
- package/index.ts +11 -0
- package/integration.ts +103 -0
- package/package.json +52 -0
- package/schemas.ts +13 -0
- package/src/assets/favicon.svg +27 -0
- package/src/assets/og-image.png +0 -0
- package/src/components/MovedIndicator.astro +10 -0
- package/src/components/Radar.astro +269 -0
- package/src/components/RadarLegend.astro +120 -0
- package/src/components/RingBadge.astro +49 -0
- package/src/components/TechnologyCard.astro +46 -0
- package/src/components/ThemeToggle.astro +115 -0
- package/src/layouts/Base.astro +155 -0
- package/src/lib/radar.ts +124 -0
- package/src/pages/404.astro +36 -0
- package/src/pages/index.astro +205 -0
- package/src/pages/segments/[segment].astro +85 -0
- package/src/pages/technology/[...slug].astro +113 -0
- package/src/styles/global.css +37 -0
- package/src/themes/catppuccin-mocha.css +55 -0
- package/src/themes/default.css +171 -0
- package/virtual.d.ts +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dennis Irsigler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# @dirsigler/astro-techradar
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@dirsigler/astro-techradar)
|
|
4
|
+
[](https://astro.build)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
An [Astro](https://astro.build) integration that adds a complete, interactive technology radar to your site. Track technology adoption decisions across your organization with a visual radar chart, categorized segments, and detailed technology pages — all driven by simple Markdown files.
|
|
8
|
+
|
|
9
|
+
**[View Live Demo](https://demo.techradar.irsigler.dev)**
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Preview
|
|
14
|
+
|
|
15
|
+
| Radar Overview | Technology Detail |
|
|
16
|
+
| :----------------------------------------------: | :--------------------------------------------------------: |
|
|
17
|
+
|  |  |
|
|
18
|
+
|  |  |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Interactive SVG Radar** — Technologies plotted across four rings (Adopt, Trial, Assess, Hold) with hover tooltips and click-through navigation
|
|
25
|
+
- **Markdown-Driven** — Add technologies by dropping `.md` files into `segments/`. No code changes needed
|
|
26
|
+
- **Theming & Color Mode** — Ships with a default theme and [Catppuccin Mocha](https://catppuccin.com). Light/dark toggle, lockable color mode, or create your own theme with CSS custom properties
|
|
27
|
+
- **Fully Static & Fast** — Builds to plain HTML/CSS/JS. Deploy anywhere
|
|
28
|
+
- **Movement Indicators** — Mark technologies as moved in/out to highlight recent changes
|
|
29
|
+
- **SEO Ready** — Open Graph, Twitter Cards, canonical URLs, and custom 404 page
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @dirsigler/astro-techradar astro
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Add the integration
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
// astro.config.mjs
|
|
45
|
+
import { defineConfig } from "astro/config";
|
|
46
|
+
import techradar from "@dirsigler/astro-techradar";
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
site: "https://your-site.example.com",
|
|
50
|
+
integrations: [
|
|
51
|
+
techradar({
|
|
52
|
+
title: "Tech Radar",
|
|
53
|
+
}),
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Define the content collections
|
|
59
|
+
|
|
60
|
+
Create `src/content.config.ts`:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { defineCollection } from "astro:content";
|
|
64
|
+
import { glob } from "astro/loaders";
|
|
65
|
+
import { segmentSchema, technologySchema } from "@dirsigler/astro-techradar/schemas";
|
|
66
|
+
|
|
67
|
+
const segments = defineCollection({
|
|
68
|
+
loader: glob({ pattern: "*/index.md", base: "./segments" }),
|
|
69
|
+
schema: segmentSchema,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const technologies = defineCollection({
|
|
73
|
+
loader: glob({ pattern: "*/!(index).md", base: "./segments" }),
|
|
74
|
+
schema: technologySchema,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const collections = { segments, technologies };
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Add your content
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
segments/
|
|
84
|
+
├── cloud/
|
|
85
|
+
│ ├── index.md # Segment metadata
|
|
86
|
+
│ ├── kubernetes.md
|
|
87
|
+
│ └── terraform.md
|
|
88
|
+
├── frameworks/
|
|
89
|
+
│ ├── index.md
|
|
90
|
+
│ ├── astro.md
|
|
91
|
+
│ └── react.md
|
|
92
|
+
└── ...
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each segment needs an `index.md`:
|
|
96
|
+
|
|
97
|
+
```markdown
|
|
98
|
+
---
|
|
99
|
+
title: Cloud
|
|
100
|
+
color: "#3b82f6"
|
|
101
|
+
order: 1
|
|
102
|
+
---
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Each technology file:
|
|
106
|
+
|
|
107
|
+
```markdown
|
|
108
|
+
---
|
|
109
|
+
title: Kubernetes
|
|
110
|
+
ring: adopt
|
|
111
|
+
moved: 0
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
Your description in Markdown. Explain why this technology is in this ring
|
|
115
|
+
and what your experience has been.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 5. Run
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx astro dev
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
All options are passed to the `techradar()` integration in `astro.config.mjs`:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
techradar({
|
|
132
|
+
// Required
|
|
133
|
+
title: "Tech Radar",
|
|
134
|
+
|
|
135
|
+
// Optional
|
|
136
|
+
basePath: "/techradar", // Mount under a sub-path (e.g. acme.com/techradar/)
|
|
137
|
+
logo: "/logo.svg", // Path to logo in public/
|
|
138
|
+
footerText: "Built by the Platform Team", // Supports HTML
|
|
139
|
+
repositoryUrl: "https://github.com/your-org/your-radar",
|
|
140
|
+
editBaseUrl: "https://github.com/your-org/your-radar/edit/main/segments",
|
|
141
|
+
theme: "default", // 'default' | 'catppuccin-mocha' | path to custom CSS
|
|
142
|
+
color: {
|
|
143
|
+
toggle: true, // Show the light/dark mode toggle (default: true)
|
|
144
|
+
mode: "system", // 'light' | 'dark' | 'system' (default: 'system')
|
|
145
|
+
},
|
|
146
|
+
socialLinks: [
|
|
147
|
+
{
|
|
148
|
+
label: "GitHub",
|
|
149
|
+
href: "https://github.com/your-org/your-radar",
|
|
150
|
+
icon: "github", // Lucide icon name (requires @iconify-json/lucide)
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Social Link Icons
|
|
157
|
+
|
|
158
|
+
Social links can optionally display [Lucide](https://lucide.dev) icons. If you use the `icon` field, install the icon set:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install @iconify-json/lucide
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
socialLinks: [
|
|
166
|
+
{ label: "GitHub", href: "https://github.com/your-org", icon: "github" },
|
|
167
|
+
],
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Technology Frontmatter
|
|
171
|
+
|
|
172
|
+
| Field | Type | Description |
|
|
173
|
+
| :------ | :----------------------------------------- | :---------------------------------------------- |
|
|
174
|
+
| `title` | `string` | Display name |
|
|
175
|
+
| `ring` | `'adopt' \| 'trial' \| 'assess' \| 'hold'` | Which ring the technology belongs to |
|
|
176
|
+
| `moved` | `-1 \| 0 \| 1` | Movement indicator (-1 = out, 0 = none, 1 = in) |
|
|
177
|
+
|
|
178
|
+
### Segment Frontmatter
|
|
179
|
+
|
|
180
|
+
| Field | Type | Description |
|
|
181
|
+
| :------ | :------- | :--------------------------- |
|
|
182
|
+
| `title` | `string` | Segment display name |
|
|
183
|
+
| `color` | `string` | Hex color (e.g. `"#3b82f6"`) |
|
|
184
|
+
| `order` | `number` | Quadrant position (1-4) |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Theming
|
|
189
|
+
|
|
190
|
+
Themes are CSS files defining `--radar-*` custom properties.
|
|
191
|
+
|
|
192
|
+
| Theme | Description |
|
|
193
|
+
| :----------------- | :---------------------------------------------------- |
|
|
194
|
+
| `default` | Clean light/dark theme (follows system preference) |
|
|
195
|
+
| `catppuccin-mocha` | [Catppuccin Mocha](https://catppuccin.com) dark theme |
|
|
196
|
+
|
|
197
|
+
To create a custom theme, copy the [default theme](src/themes/default.css), save it in your project, and point to it:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
techradar({
|
|
201
|
+
theme: "./src/my-theme.css",
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Color Mode
|
|
206
|
+
|
|
207
|
+
Control the light/dark mode behavior with the `color` option:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
// Default — toggle visible, follows system preference
|
|
211
|
+
techradar({ title: "Tech Radar" });
|
|
212
|
+
|
|
213
|
+
// Lock to dark mode, no toggle
|
|
214
|
+
techradar({
|
|
215
|
+
title: "Tech Radar",
|
|
216
|
+
color: { toggle: false, mode: "dark" },
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Lock to light mode, no toggle
|
|
220
|
+
techradar({
|
|
221
|
+
title: "Tech Radar",
|
|
222
|
+
color: { toggle: false, mode: "light" },
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Field | Type | Default | Description |
|
|
227
|
+
| :------- | :------------------------------ | :--------- | :--------------------------------------------------- |
|
|
228
|
+
| `toggle` | `boolean` | `true` | Show the light/dark mode toggle in the header |
|
|
229
|
+
| `mode` | `'light' \| 'dark' \| 'system'` | `'system'` | Color mode — locks the mode when `toggle` is `false` |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Base Path
|
|
234
|
+
|
|
235
|
+
Mount the radar under a sub-path when embedding it into a larger Astro site:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
// Serves the radar at acme.com/techradar/
|
|
239
|
+
techradar({
|
|
240
|
+
title: "ACME Tech Radar",
|
|
241
|
+
basePath: "/techradar",
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
All routes and internal links are automatically prefixed. This works alongside Astro's own `base` config for full flexibility.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Example
|
|
250
|
+
|
|
251
|
+
See the [techradar-demo](https://github.com/dirsigler/techradar-demo) repository for a complete working example.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
[MIT](LICENSE) — Dennis Irsigler
|
package/config.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export interface SocialLink {
|
|
2
|
+
label: string;
|
|
3
|
+
href: string;
|
|
4
|
+
/** Lucide icon name (e.g. "github", "twitter", "globe"). Falls back to label text if unrecognized. */
|
|
5
|
+
icon?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ColorModeConfig {
|
|
9
|
+
/** Show the light/dark mode toggle in the header. Default: true */
|
|
10
|
+
toggle?: boolean;
|
|
11
|
+
/** Color mode preference. When toggle is false this locks the mode. Default: 'system' */
|
|
12
|
+
mode?: 'light' | 'dark' | 'system';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ResolvedColorMode {
|
|
16
|
+
toggle: boolean;
|
|
17
|
+
mode: 'light' | 'dark' | 'system';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TechRadarUserConfig {
|
|
21
|
+
/** Navbar title text */
|
|
22
|
+
title: string;
|
|
23
|
+
|
|
24
|
+
/** Main page headline. Falls back to title if not set. */
|
|
25
|
+
headline?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* URL path prefix where the tech radar is mounted (e.g. "/techradar").
|
|
29
|
+
* Useful when embedding the radar into a larger Astro site.
|
|
30
|
+
* Default: "" (root)
|
|
31
|
+
*/
|
|
32
|
+
basePath?: string;
|
|
33
|
+
|
|
34
|
+
/** Logo image — either a path in public/ (e.g. "/logo.svg") or an external HTTPS URL. Omit to show title only. */
|
|
35
|
+
logo?: string;
|
|
36
|
+
|
|
37
|
+
/** Footer text. Supports simple HTML. */
|
|
38
|
+
footerText?: string;
|
|
39
|
+
|
|
40
|
+
/** Show "Edit this page" links on technology pages. Default: true */
|
|
41
|
+
allowEditing?: boolean;
|
|
42
|
+
|
|
43
|
+
/** Base URL for "Edit" links on technology pages (e.g. "https://github.com/org/repo/edit/main/segments"). */
|
|
44
|
+
editBaseUrl?: string;
|
|
45
|
+
|
|
46
|
+
/** Social links shown in the footer. */
|
|
47
|
+
socialLinks?: SocialLink[];
|
|
48
|
+
|
|
49
|
+
/** Theme name — matches a built-in CSS file ('default' | 'catppuccin-mocha') or a path to a custom CSS file. Default: 'default' */
|
|
50
|
+
theme?: string;
|
|
51
|
+
|
|
52
|
+
/** Color mode configuration. */
|
|
53
|
+
color?: ColorModeConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ResolvedConfig {
|
|
57
|
+
title: string;
|
|
58
|
+
headline: string;
|
|
59
|
+
/** Normalized base path with leading slash, no trailing slash. Empty string when mounted at root. */
|
|
60
|
+
basePath: string;
|
|
61
|
+
logo?: string;
|
|
62
|
+
footerText: string;
|
|
63
|
+
editBaseUrl?: string;
|
|
64
|
+
socialLinks: SocialLink[];
|
|
65
|
+
theme: string;
|
|
66
|
+
color: ResolvedColorMode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Normalize a user-supplied path: ensure leading slash, strip trailing slash. Returns "" for root. */
|
|
70
|
+
function normalizeBasePath(raw?: string): string {
|
|
71
|
+
if (!raw) return '';
|
|
72
|
+
// Strip leading/trailing slashes, then re-add leading slash
|
|
73
|
+
const trimmed = raw.replace(/^\/+|\/+$/g, '');
|
|
74
|
+
return trimmed ? `/${trimmed}` : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function resolveConfig(user: TechRadarUserConfig): ResolvedConfig {
|
|
78
|
+
return {
|
|
79
|
+
title: user.title,
|
|
80
|
+
headline: user.headline ?? user.title,
|
|
81
|
+
basePath: normalizeBasePath(user.basePath),
|
|
82
|
+
logo: user.logo,
|
|
83
|
+
footerText: user.footerText ?? '',
|
|
84
|
+
editBaseUrl:
|
|
85
|
+
(user.allowEditing ?? true) === false
|
|
86
|
+
? undefined
|
|
87
|
+
: user.editBaseUrl,
|
|
88
|
+
socialLinks: user.socialLinks ?? [],
|
|
89
|
+
theme: user.theme ?? 'default',
|
|
90
|
+
color: {
|
|
91
|
+
toggle: user.color?.toggle ?? true,
|
|
92
|
+
mode: user.color?.mode ?? 'system',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AstroIntegration } from 'astro';
|
|
2
|
+
import type { TechRadarUserConfig } from './config';
|
|
3
|
+
import { createIntegration } from './integration';
|
|
4
|
+
|
|
5
|
+
export default function techradar(
|
|
6
|
+
userConfig: TechRadarUserConfig,
|
|
7
|
+
): AstroIntegration {
|
|
8
|
+
return createIntegration(userConfig);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type { TechRadarUserConfig, ResolvedConfig, SocialLink } from './config';
|
package/integration.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { AstroIntegration } from 'astro';
|
|
2
|
+
import type { TechRadarUserConfig } from './config';
|
|
3
|
+
import { resolveConfig } from './config';
|
|
4
|
+
import icon from 'astro-icon';
|
|
5
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { copyFileSync, mkdirSync, readFileSync, existsSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
const PKG_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
export function createIntegration(
|
|
13
|
+
userConfig: TechRadarUserConfig,
|
|
14
|
+
): AstroIntegration {
|
|
15
|
+
const config = resolveConfig(userConfig);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: '@dirsigler/astro-techradar',
|
|
19
|
+
hooks: {
|
|
20
|
+
'astro:config:setup'({ injectRoute, updateConfig, config: astroConfig }) {
|
|
21
|
+
const bp = config.basePath; // e.g. "" or "/techradar"
|
|
22
|
+
|
|
23
|
+
// Inject all routes (prefixed with basePath)
|
|
24
|
+
injectRoute({
|
|
25
|
+
pattern: `${bp}/`,
|
|
26
|
+
entrypoint: path.join(PKG_DIR, 'src/pages/index.astro'),
|
|
27
|
+
});
|
|
28
|
+
injectRoute({
|
|
29
|
+
pattern: `${bp}/404`,
|
|
30
|
+
entrypoint: path.join(PKG_DIR, 'src/pages/404.astro'),
|
|
31
|
+
});
|
|
32
|
+
injectRoute({
|
|
33
|
+
pattern: `${bp}/segments/[segment]`,
|
|
34
|
+
entrypoint: path.join(PKG_DIR, 'src/pages/segments/[segment].astro'),
|
|
35
|
+
});
|
|
36
|
+
injectRoute({
|
|
37
|
+
pattern: `${bp}/technology/[...slug]`,
|
|
38
|
+
entrypoint: path.join(
|
|
39
|
+
PKG_DIR,
|
|
40
|
+
'src/pages/technology/[...slug].astro',
|
|
41
|
+
),
|
|
42
|
+
});
|
|
43
|
+
// Copy static assets into the consumer's public directory
|
|
44
|
+
const publicDir = fileURLToPath(astroConfig.publicDir);
|
|
45
|
+
mkdirSync(publicDir, { recursive: true });
|
|
46
|
+
for (const file of ['favicon.svg', 'og-image.png']) {
|
|
47
|
+
copyFileSync(
|
|
48
|
+
path.join(PKG_DIR, 'src/assets', file),
|
|
49
|
+
path.join(publicDir, file),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Resolve theme CSS
|
|
54
|
+
let themeCSS = '';
|
|
55
|
+
const builtinPath = path.join(
|
|
56
|
+
PKG_DIR,
|
|
57
|
+
`src/themes/${config.theme}.css`,
|
|
58
|
+
);
|
|
59
|
+
if (existsSync(builtinPath)) {
|
|
60
|
+
themeCSS = readFileSync(builtinPath, 'utf-8');
|
|
61
|
+
} else if (existsSync(config.theme)) {
|
|
62
|
+
// User provided an absolute or relative path to a custom theme
|
|
63
|
+
themeCSS = readFileSync(config.theme, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Virtual modules for config and theme
|
|
67
|
+
const VIRTUAL_CONFIG = 'virtual:techradar/config';
|
|
68
|
+
const RESOLVED_CONFIG = '\0' + VIRTUAL_CONFIG;
|
|
69
|
+
const VIRTUAL_THEME = 'virtual:techradar/theme';
|
|
70
|
+
const RESOLVED_THEME = '\0' + VIRTUAL_THEME;
|
|
71
|
+
|
|
72
|
+
// Register astro-icon if not already added by the user
|
|
73
|
+
const hasIcon = astroConfig.integrations.some((i) => i.name === 'astro-icon');
|
|
74
|
+
if (!hasIcon) {
|
|
75
|
+
updateConfig({ integrations: [icon()] });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
updateConfig({
|
|
79
|
+
vite: {
|
|
80
|
+
plugins: [
|
|
81
|
+
tailwindcss(),
|
|
82
|
+
{
|
|
83
|
+
name: 'techradar-virtual-modules',
|
|
84
|
+
resolveId(id: string) {
|
|
85
|
+
if (id === VIRTUAL_CONFIG) return RESOLVED_CONFIG;
|
|
86
|
+
if (id === VIRTUAL_THEME) return RESOLVED_THEME;
|
|
87
|
+
},
|
|
88
|
+
load(id: string) {
|
|
89
|
+
if (id === RESOLVED_CONFIG) {
|
|
90
|
+
return `export default ${JSON.stringify(config)}`;
|
|
91
|
+
}
|
|
92
|
+
if (id === RESOLVED_THEME) {
|
|
93
|
+
return `export default ${JSON.stringify(themeCSS)}`;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dirsigler/astro-techradar",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "An interactive technology radar Astro integration — track technology adoption across your organization",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Dennis Irsigler <dennis@irsigler.dev>",
|
|
8
|
+
"homepage": "https://github.com/dirsigler/techradar",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/dirsigler/techradar.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"astro-integration",
|
|
15
|
+
"techradar",
|
|
16
|
+
"technology-radar",
|
|
17
|
+
"radar",
|
|
18
|
+
"engineering"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./index.ts",
|
|
22
|
+
"./schemas": "./schemas.ts"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md",
|
|
27
|
+
"index.ts",
|
|
28
|
+
"integration.ts",
|
|
29
|
+
"config.ts",
|
|
30
|
+
"schemas.ts",
|
|
31
|
+
"virtual.d.ts",
|
|
32
|
+
"src/**/*"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.12.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@iconify-json/lucide": "^1.2.98",
|
|
39
|
+
"astro": "^6.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"@iconify-json/lucide": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
48
|
+
"@tailwindcss/vite": "^4.2.1",
|
|
49
|
+
"astro-icon": "^1.1.5",
|
|
50
|
+
"tailwindcss": "^4.2.1"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/schemas.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'astro:content';
|
|
2
|
+
|
|
3
|
+
export const segmentSchema = z.object({
|
|
4
|
+
title: z.string(),
|
|
5
|
+
order: z.number().int().min(1).max(4),
|
|
6
|
+
color: z.string().regex(/^#[0-9a-fA-F]{6}$/),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const technologySchema = z.object({
|
|
10
|
+
title: z.string(),
|
|
11
|
+
ring: z.enum(['adopt', 'trial', 'assess', 'hold']),
|
|
12
|
+
moved: z.number().int().min(-1).max(1).default(0),
|
|
13
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<!-- Concentric rings -->
|
|
3
|
+
<circle cx="16" cy="16" r="15" fill="none" stroke="#cbd5e1" stroke-width="0.75" opacity="0.4"/>
|
|
4
|
+
<circle cx="16" cy="16" r="11" fill="none" stroke="#cbd5e1" stroke-width="0.75" opacity="0.5"/>
|
|
5
|
+
<circle cx="16" cy="16" r="7" fill="none" stroke="#cbd5e1" stroke-width="0.75" opacity="0.6"/>
|
|
6
|
+
<circle cx="16" cy="16" r="3" fill="none" stroke="#cbd5e1" stroke-width="0.75" opacity="0.7"/>
|
|
7
|
+
|
|
8
|
+
<!-- Hold ring (outer) — red dots -->
|
|
9
|
+
<circle cx="5" cy="10" r="1.4" fill="#dc2626"/>
|
|
10
|
+
<circle cx="26" cy="20" r="1.2" fill="#dc2626"/>
|
|
11
|
+
<circle cx="10" cy="27" r="1.1" fill="#dc2626"/>
|
|
12
|
+
|
|
13
|
+
<!-- Assess ring — yellow dots -->
|
|
14
|
+
<circle cx="22" cy="9" r="1.3" fill="#ca8a04"/>
|
|
15
|
+
<circle cx="8" cy="19" r="1.2" fill="#ca8a04"/>
|
|
16
|
+
<circle cx="20" cy="25" r="1.0" fill="#ca8a04"/>
|
|
17
|
+
|
|
18
|
+
<!-- Trial ring — blue dots -->
|
|
19
|
+
<circle cx="12" cy="10" r="1.3" fill="#2563eb"/>
|
|
20
|
+
<circle cx="22" cy="15" r="1.1" fill="#2563eb"/>
|
|
21
|
+
<circle cx="11" cy="22" r="1.2" fill="#2563eb"/>
|
|
22
|
+
|
|
23
|
+
<!-- Adopt ring (inner) — green dots -->
|
|
24
|
+
<circle cx="16" cy="13" r="1.4" fill="#16a34a"/>
|
|
25
|
+
<circle cx="14" cy="18" r="1.2" fill="#16a34a"/>
|
|
26
|
+
<circle cx="19" cy="17" r="1.1" fill="#16a34a"/>
|
|
27
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
moved: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { moved } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
{moved === 1 && <span style="color: var(--radar-moved-in);" title="Moved in">▲</span>}
|
|
10
|
+
{moved === -1 && <span style="color: var(--radar-moved-out);" title="Moved out">▼</span>}
|