@ujjan/built-badge 0.1.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/README.md +125 -0
- package/dist/ujjan-badge.css +99 -0
- package/dist/ujjan-badge.png +0 -0
- package/package.json +32 -0
- package/snippets/html.html +12 -0
- package/snippets/php.php +28 -0
- package/snippets/react.tsx +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Ujjan Built Badge
|
|
2
|
+
|
|
3
|
+
Reusable "Built at Ujjan.com" badge kit for adding Ujjan attribution to project footers across web codebases.
|
|
4
|
+
|
|
5
|
+
## Rule
|
|
6
|
+
|
|
7
|
+
Place the badge inside a project's footer component or footer partial only.
|
|
8
|
+
If a page does not render a footer, it should not render this badge.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
Copy these files into your project:
|
|
13
|
+
|
|
14
|
+
- `dist/ujjan-badge.css`
|
|
15
|
+
- `dist/ujjan-badge.png`
|
|
16
|
+
|
|
17
|
+
Load the CSS from the page or layout that owns the footer:
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<link rel="stylesheet" href="/assets/ujjan-badge.css">
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then place the badge markup inside the footer:
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<a
|
|
27
|
+
class="ujjan-built-badge"
|
|
28
|
+
href="https://ujjan.com/work/promptiles"
|
|
29
|
+
target="_blank"
|
|
30
|
+
rel="noopener noreferrer"
|
|
31
|
+
aria-label="Built at Ujjan.com"
|
|
32
|
+
>
|
|
33
|
+
<img src="/assets/ujjan-badge.png" alt="" width="25" height="30">
|
|
34
|
+
<span>Built at Ujjan.com</span>
|
|
35
|
+
</a>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use the project case-study slug after `/work/`, for example:
|
|
39
|
+
|
|
40
|
+
```txt
|
|
41
|
+
https://ujjan.com/work/promptiles
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If the case study is not published yet, use the homepage fallback:
|
|
45
|
+
|
|
46
|
+
```txt
|
|
47
|
+
https://ujjan.com
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Plain HTML
|
|
51
|
+
|
|
52
|
+
Use [snippets/html.html](snippets/html.html) inside your footer:
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<footer>
|
|
56
|
+
<!-- Ujjan badge goes here -->
|
|
57
|
+
</footer>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## PHP
|
|
61
|
+
|
|
62
|
+
Copy [snippets/php.php](snippets/php.php) into a partial, then include it only from footer templates:
|
|
63
|
+
|
|
64
|
+
```php
|
|
65
|
+
<?php $ujjanCaseStudySlug = 'promptiles'; ?>
|
|
66
|
+
<?php include __DIR__ . '/ujjan-built-badge.php'; ?>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If `$ujjanCaseStudySlug` is not set or is empty, the badge links to `https://ujjan.com`.
|
|
70
|
+
|
|
71
|
+
## React
|
|
72
|
+
|
|
73
|
+
Copy [snippets/react.tsx](snippets/react.tsx) into your component library, import the CSS once, and render it from your footer component:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import "./ujjan-badge.css";
|
|
77
|
+
import { UjjanBuiltBadge } from "./UjjanBuiltBadge";
|
|
78
|
+
|
|
79
|
+
export function Footer() {
|
|
80
|
+
return (
|
|
81
|
+
<footer>
|
|
82
|
+
<UjjanBuiltBadge
|
|
83
|
+
caseStudySlug="promptiles"
|
|
84
|
+
logoSrc="/assets/ujjan-badge.png"
|
|
85
|
+
/>
|
|
86
|
+
</footer>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Omit `caseStudySlug` until the case study is live:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<UjjanBuiltBadge logoSrc="/assets/ujjan-badge.png" />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Files
|
|
98
|
+
|
|
99
|
+
- `dist/ujjan-badge.css` - standalone badge styles.
|
|
100
|
+
- `dist/ujjan-badge.png` - badge logo asset.
|
|
101
|
+
- `snippets/html.html` - plain HTML footer snippet.
|
|
102
|
+
- `snippets/php.php` - PHP footer partial snippet.
|
|
103
|
+
- `snippets/react.tsx` - React/Next-compatible component.
|
|
104
|
+
|
|
105
|
+
## Theme Hooks
|
|
106
|
+
|
|
107
|
+
The CSS uses custom properties so each project can tune the badge without editing this package:
|
|
108
|
+
|
|
109
|
+
```css
|
|
110
|
+
:root {
|
|
111
|
+
--ujjan-badge-bg: #1e2126;
|
|
112
|
+
--ujjan-badge-border: #505050;
|
|
113
|
+
--ujjan-badge-text: #fff;
|
|
114
|
+
--ujjan-badge-shine: #fff;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Accessibility
|
|
119
|
+
|
|
120
|
+
The badge is a normal link with `target="_blank"` and `rel="noopener noreferrer"`.
|
|
121
|
+
The logo image is decorative and uses an empty `alt` value because the link already has an accessible label.
|
|
122
|
+
|
|
123
|
+
## Versioning
|
|
124
|
+
|
|
125
|
+
Use Git tags for stable drops, starting with `v0.1.0`.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
.ujjan-built-badge {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 6px;
|
|
6
|
+
min-height: 40px;
|
|
7
|
+
width: fit-content;
|
|
8
|
+
padding: 0 18px;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
border: 1.5px solid var(--ujjan-badge-border, #505050);
|
|
11
|
+
border-radius: var(--ujjan-badge-radius, 5px);
|
|
12
|
+
background: var(--ujjan-badge-bg, #1e2126);
|
|
13
|
+
color: var(--ujjan-badge-text, #fff);
|
|
14
|
+
font: 500 14px/1.2 var(--ujjan-badge-font, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
isolation: isolate;
|
|
18
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ujjan-built-badge::before,
|
|
22
|
+
.ujjan-built-badge::after {
|
|
23
|
+
content: "";
|
|
24
|
+
position: absolute;
|
|
25
|
+
top: -100%;
|
|
26
|
+
height: 300%;
|
|
27
|
+
transform: rotate(45deg);
|
|
28
|
+
background: var(--ujjan-badge-shine, #fff);
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
z-index: -1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ujjan-built-badge::before {
|
|
34
|
+
left: -150px;
|
|
35
|
+
width: 30%;
|
|
36
|
+
opacity: 0.4;
|
|
37
|
+
animation: ujjan-badge-shine-wide 5s ease 5s infinite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ujjan-built-badge::after {
|
|
41
|
+
left: -50px;
|
|
42
|
+
width: 10%;
|
|
43
|
+
opacity: 0.5;
|
|
44
|
+
animation: ujjan-badge-shine-narrow 5s ease 5s infinite;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.ujjan-built-badge:hover {
|
|
48
|
+
color: var(--ujjan-badge-text, #fff);
|
|
49
|
+
border-color: var(--ujjan-badge-hover-border, var(--ujjan-badge-border, #505050));
|
|
50
|
+
box-shadow: 0 0 20px 5px var(--ujjan-badge-shadow, rgba(252, 217, 184, 0.1));
|
|
51
|
+
transform: scale(1.04);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ujjan-built-badge:focus-visible {
|
|
55
|
+
outline: 2px solid var(--ujjan-badge-focus, #46fea5);
|
|
56
|
+
outline-offset: 3px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ujjan-built-badge__logo {
|
|
60
|
+
display: block;
|
|
61
|
+
width: 25px;
|
|
62
|
+
height: 30px;
|
|
63
|
+
flex: 0 0 auto;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.ujjan-built-badge__text {
|
|
67
|
+
white-space: nowrap;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@keyframes ujjan-badge-shine-wide {
|
|
71
|
+
0% {
|
|
72
|
+
left: -150px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
20%,
|
|
76
|
+
100% {
|
|
77
|
+
left: 300px;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@keyframes ujjan-badge-shine-narrow {
|
|
82
|
+
0% {
|
|
83
|
+
left: -50px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
20%,
|
|
87
|
+
100% {
|
|
88
|
+
left: 400px;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media (prefers-reduced-motion: reduce) {
|
|
93
|
+
.ujjan-built-badge,
|
|
94
|
+
.ujjan-built-badge::before,
|
|
95
|
+
.ujjan-built-badge::after {
|
|
96
|
+
animation: none;
|
|
97
|
+
transition: none;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ujjan/built-badge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Reusable footer badge kit for Built at Ujjan.com attribution.",
|
|
5
|
+
"homepage": "https://ujjan.com",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Ujjan-com/ujjan-built-badge.git"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"snippets",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
"./css": "./dist/ujjan-badge.css",
|
|
17
|
+
"./image": "./dist/ujjan-badge.png",
|
|
18
|
+
"./html": "./snippets/html.html",
|
|
19
|
+
"./php": "./snippets/php.php",
|
|
20
|
+
"./react": "./snippets/react.tsx"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": [
|
|
23
|
+
"./dist/ujjan-badge.css"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ujjan",
|
|
27
|
+
"badge",
|
|
28
|
+
"footer",
|
|
29
|
+
"attribution"
|
|
30
|
+
],
|
|
31
|
+
"license": "UNLICENSED"
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!-- Place this inside the project's footer only. -->
|
|
2
|
+
<!-- Use https://ujjan.com when the project case study is not published yet. -->
|
|
3
|
+
<a
|
|
4
|
+
class="ujjan-built-badge"
|
|
5
|
+
href="https://ujjan.com/work/promptiles"
|
|
6
|
+
target="_blank"
|
|
7
|
+
rel="noopener noreferrer"
|
|
8
|
+
aria-label="Built at Ujjan.com"
|
|
9
|
+
>
|
|
10
|
+
<img class="ujjan-built-badge__logo" src="/assets/ujjan-badge.png" alt="" width="25" height="30">
|
|
11
|
+
<span class="ujjan-built-badge__text">Built at Ujjan.com</span>
|
|
12
|
+
</a>
|
package/snippets/php.php
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Place this partial inside the project's footer only.
|
|
4
|
+
*
|
|
5
|
+
* Optional:
|
|
6
|
+
* $ujjanCaseStudySlug = 'promptiles';
|
|
7
|
+
*
|
|
8
|
+
* If no slug is provided, the badge links to https://ujjan.com.
|
|
9
|
+
*/
|
|
10
|
+
$ujjanBadgeBaseUrl = 'https://ujjan.com';
|
|
11
|
+
$ujjanBadgeSlug = isset($ujjanCaseStudySlug) && is_string($ujjanCaseStudySlug)
|
|
12
|
+
? trim($ujjanCaseStudySlug, " \t\n\r\0\x0B/")
|
|
13
|
+
: '';
|
|
14
|
+
$ujjanBadgeSlug = preg_replace('#^work/#', '', $ujjanBadgeSlug);
|
|
15
|
+
$ujjanBadgeHref = $ujjanBadgeSlug !== ''
|
|
16
|
+
? $ujjanBadgeBaseUrl . '/work/' . rawurlencode($ujjanBadgeSlug)
|
|
17
|
+
: $ujjanBadgeBaseUrl;
|
|
18
|
+
?>
|
|
19
|
+
<a
|
|
20
|
+
class="ujjan-built-badge"
|
|
21
|
+
href="<?= htmlspecialchars($ujjanBadgeHref, ENT_QUOTES, 'UTF-8') ?>"
|
|
22
|
+
target="_blank"
|
|
23
|
+
rel="noopener noreferrer"
|
|
24
|
+
aria-label="Built at Ujjan.com"
|
|
25
|
+
>
|
|
26
|
+
<img class="ujjan-built-badge__logo" src="/assets/ujjan-badge.png" alt="" width="25" height="30">
|
|
27
|
+
<span class="ujjan-built-badge__text">Built at Ujjan.com</span>
|
|
28
|
+
</a>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
type UjjanBuiltBadgeProps = {
|
|
2
|
+
caseStudySlug?: string;
|
|
3
|
+
className?: string;
|
|
4
|
+
logoSrc?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const UJJAN_BASE_URL = "https://ujjan.com";
|
|
8
|
+
|
|
9
|
+
function getUjjanBadgeHref(caseStudySlug?: string) {
|
|
10
|
+
const slug = caseStudySlug
|
|
11
|
+
?.trim()
|
|
12
|
+
.replace(/^\/+|\/+$/g, "")
|
|
13
|
+
.replace(/^work\//, "");
|
|
14
|
+
|
|
15
|
+
return slug ? `${UJJAN_BASE_URL}/work/${encodeURIComponent(slug)}` : UJJAN_BASE_URL;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function UjjanBuiltBadge({
|
|
19
|
+
caseStudySlug,
|
|
20
|
+
className = "",
|
|
21
|
+
logoSrc = "/assets/ujjan-badge.png",
|
|
22
|
+
}: UjjanBuiltBadgeProps) {
|
|
23
|
+
const classes = ["ujjan-built-badge", className].filter(Boolean).join(" ");
|
|
24
|
+
const href = getUjjanBadgeHref(caseStudySlug);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<a
|
|
28
|
+
className={classes}
|
|
29
|
+
href={href}
|
|
30
|
+
target="_blank"
|
|
31
|
+
rel="noopener noreferrer"
|
|
32
|
+
aria-label="Built at Ujjan.com"
|
|
33
|
+
>
|
|
34
|
+
<img
|
|
35
|
+
className="ujjan-built-badge__logo"
|
|
36
|
+
src={logoSrc}
|
|
37
|
+
alt=""
|
|
38
|
+
width={25}
|
|
39
|
+
height={30}
|
|
40
|
+
/>
|
|
41
|
+
<span className="ujjan-built-badge__text">Built at Ujjan.com</span>
|
|
42
|
+
</a>
|
|
43
|
+
);
|
|
44
|
+
}
|