39 Commits

Author SHA1 Message Date
Seth Cottle
573e1c21ad Update VERSION.md 2025-03-25 20:42:44 -04:00
Seth Cottle
d6fd8a28b1 Updates Signal Brand Color + Adds Alt 2025-03-25 20:34:53 -04:00
Seth Cottle
23f2f1095f Merge pull request #151 from lllahaye/feature/Docker-support
Add Docker Support to the Project
2025-03-25 19:59:20 -04:00
Seth Cottle
915605c637 Update contrast-check.yml 2025-03-17 20:11:29 -04:00
Seth Cottle
a292324f01 Remove Script 2025-03-17 20:07:29 -04:00
Seth Cottle
2a384ae6ba Update contrast-check.yml 2025-03-17 18:51:05 -04:00
Seth Cottle
3e580dbca6 Create contrast-check.yml 2025-03-17 18:43:22 -04:00
Seth Cottle
3e71e529e2 revert 2025-03-17 18:41:30 -04:00
Seth Cottle
7eff8dee81 manual trigger 2025-03-17 18:38:43 -04:00
Seth Cottle
61ff1bff39 Merge pull request #152 from sethcottle/actions
Contrast Workflow
2025-03-17 18:33:51 -04:00
Seth Cottle
a13ec80c62 Contrast Workflow 2025-03-17 18:32:07 -04:00
Lian Leon
b29c8b23d5 Add Docker support 2025-03-17 13:18:40 -04:00
Lian Leon
8defce6119 Add Docker support section to README.md 2025-03-17 13:08:35 -04:00
Seth Cottle
24b374f557 LittleLink Extended 2025-03-10 12:10:27 -04:00
Seth Cottle
df23da72c2 Update VERSION.md 2025-03-07 15:19:50 -05:00
Seth Cottle
13bdbe44ae Update VERSION.md 2025-03-07 15:19:39 -05:00
Seth Cottle
0e2edc3135 Merge pull request #146 from ji3o/main 2025-03-04 19:07:37 -05:00
Julian Graßhoff
98b9cba737 Added missing boarder to Matrix button 2025-03-04 16:45:16 +01:00
Julian Graßhoff
54afafdf2b Added a button for Matrix network. 2025-03-04 14:53:52 +01:00
Seth Cottle
b223dbddd3 Brand Updates
- Updated Facebook Logo
- Updated Messenger Logo
- Updated Messenger Button Color
2025-03-01 15:47:07 -05:00
Seth Cottle
4db16003e2 Update README.md 2025-02-19 15:13:10 -05:00
Seth Cottle
716d98a748 Delete directory 2025-02-16 01:59:45 -05:00
Seth Cottle
d098044f87 Updated Brands
Added `Apple Invites`, since iCloud subscribers can share their URLs with Apple and non-Apple users.

Removed `Read.cv` since it's unfortunately winding down.
2025-02-14 19:16:55 -05:00
Seth Cottle
e3ce08ce19 Update README.md 2025-01-29 08:35:47 -06:00
Seth Cottle
606fc39204 Update README.md 2025-01-29 00:28:33 -06:00
Seth Cottle
949ed19bcb Update README.md 2025-01-28 23:55:18 -06:00
Seth Cottle
b0af864541 Update VERSION.md 2025-01-28 23:52:17 -06:00
Seth Cottle
5a2ed1592a Update README.md 2025-01-28 23:50:39 -06:00
Seth Cottle
2348491382 Merge pull request #143 from timtjtim/obsidian-logo-alt 2025-01-28 10:00:28 -05:00
Tim Hitchins
aede738f1a Fix Obsidian logo alt 2025-01-28 14:49:56 +00:00
Seth Cottle
0db66a54d4 Update VERSION.md 2025-01-20 22:13:27 -05:00
Seth Cottle
2f8ddfa076 Merge pull request #138 from Omikorin/feat/youtube-alt
feat: youtube alt
2025-01-20 21:38:00 -05:00
Seth Cottle
b31d74ee46 Merge pull request #137 from rosahaj/main
Fix accessibility issues
2025-01-20 21:32:06 -05:00
Michał Korczak
460cf6a7a5 feat: youtube alt 2025-01-03 22:36:25 +00:00
rosahaj
905791e1c7 Fix accessibility issues 2024-12-22 05:43:38 +01:00
Seth Cottle
94794514ac Merge pull request #136 from nickmudry/Obsidian-Branding
Added Obsidian
2024-12-20 13:15:35 -05:00
Seth Cottle
15f8614461 Version Bump 2024-12-20 13:14:55 -05:00
Seth Cottle
568b2f6c0e Add Obsidian to index.html 2024-12-20 13:12:03 -05:00
Nick Mudry
c48c752a26 Adding Obsidian CSS and SVG 2024-12-15 15:10:35 -05:00
20 changed files with 857 additions and 68 deletions

View File

@@ -1,28 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/alpine
{
"name": "Alpine",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/base:alpine",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "uname -a",
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"extensions": [
"ritwickdey.liveserver"
]
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

42
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,42 @@
## 📄 Description
<!-- Provide a short description of the brand you're adding or the changes you're making. Include context about why this is a valuable addition. -->
---
## ✅ Contribution Checklist
Please confirm that you've met the following criteria before submitting your contribution:
- [ ] **Widespread Recognition:** The brand has widespread recognition and is suitable for the core LittleLink repository (most additions belong in [LittleLink Extended](https://github.com/sethcottle/littlelink-extended)).
- [ ] **Brand Styling Guidelines:** Youve followed the brand's official styling guidelines (if available).
- [ ] **Consistent Styling:** If no guidelines exist, the button style is consistent with the brand's public image (e.g., website, social media).
- [ ] **Icon Clarity:** The brand's logo/icon is clear and recognizable in a 24x24px format.
- [ ] If the primary logo doesnt scale well, youve adapted it using the brands social media avatar or favicon while maintaining the essence of the original logo.
- [ ] The icon is provided in `.svg` format.
- [ ] **Theme Testing:** You've tested the button against both light and dark themes:
- [ ] Manually swapped `theme-auto.css` with `theme-light.css` and `theme-dark.css` in `index.html` to check contrast or used [LittleLink Button Builder](https://builder.littlelink.io) contrast checker.
- [ ] Added a `#000000`/`#FFFFFF` stroke if necessary to improve contrast and accessibility. [LittleLink Button Builder](https://builder.littlelink.io) will automatically recommend when to add a stroke.
- [ ] **Accessibility Compliance:** The button's background and text colors meet visual accessibility standards (unless it contradicts brand guidelines).
- [ ] **Alphabetical Order:** Your contribution is alphabetically organized in `brands.css` and `index.html`.
- [ ] **Button Preview:** You've added a button preview in `index.html`.
- [ ] **Variant Naming Schema:** If adding a variant button (e.g., inverted color scheme):
- [ ] Naming follows the existing pattern (`[Brand Name] Alt` and `.button-brandname-alt`).
- [ ] Any additional icons are named according to `brandname-alt.svg` schema.
- [ ] **Proper Capitalization:**
- [ ] In `brands.css`, the brand name comment follows `/* Brand Name */` format.
- [ ] Code uses lowercase for `.button.button-brandname`.
- [ ] In `index.html`, comments reflect `<!-- Brand Name -->` format.
- [ ] Button text and `alt` attributes match the brands official capitalization.
- [ ] **PR Details:**
- [ ] Included a brief description of the brand addition.
- [ ] Included a screenshot of the new button(s).
- [ ] Provided relevant information on the brands global/regional reach or usage stats.
---
## 📸 Screenshot
<!-- Attach a screenshot of the new button(s) to ensure consistency and clarity. -->
---
## 🚀 Additional Notes
<!-- Add any other information that might help the reviewer understand the changes better (e.g., source of logo, special handling, etc.). -->

242
.github/workflows/contrast-check.yml vendored Normal file
View File

@@ -0,0 +1,242 @@
name: "Contrast Check"
on:
pull_request:
paths:
- 'css/brands.css'
workflow_dispatch: # Manual trigger for testing
jobs:
contrast-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup for PR comparison
run: |
echo "Fetching base branch for comparison"
git fetch origin ${{ github.base_ref }}
- name: Contrast Check (Review Me)
run: |
cat > contrast-check.sh << 'EOF'
#!/bin/bash
# WCAG Minimum Contrast Ratio
MIN_CONTRAST=4.5
FAILED=0
ALL_RESOLVED=1
NEEDS_MANUAL_REVIEW=0
# Only get buttons that were modified in the PR
echo "Finding changed button styles..."
BUTTON_CLASSES=$(git diff origin/$GITHUB_BASE_REF -- css/brands.css | grep -E "^\+.*\.button-[a-zA-Z0-9]+" | sed -E 's/.*\.button-([a-zA-Z0-9]+).*/\1/' | sort -u)
if [[ -z "$BUTTON_CLASSES" ]]; then
echo "✅ No button changes to check."
exit 0
fi
echo "Found button classes to check: $BUTTON_CLASSES"
echo "🔍 Auditing CSS for contrast issues..."
# Function to normalize hex colors to lowercase
normalize_color() {
local color="$1"
if [[ -n "$color" ]]; then
echo "$color" | tr '[:upper:]' '[:lower:]'
else
echo ""
fi
}
# Function to calculate luminance
get_luminance() {
local color="$1"
if [[ -z "$color" || "$color" == "#" ]]; then
echo 0
return
fi
color="${color#'#'}"
if [[ ${#color} -ne 6 ]]; then
echo 0
return
fi
r=$(printf "%d" 0x${color:0:2} 2>/dev/null || echo 0)
g=$(printf "%d" 0x${color:2:2} 2>/dev/null || echo 0)
b=$(printf "%d" 0x${color:4:2} 2>/dev/null || echo 0)
r=$(awk "BEGIN { print ($r/255 <= 0.03928) ? ($r/255)/12.92 : ((($r/255) + 0.055)/1.055) ^ 2.4 }")
g=$(awk "BEGIN { print ($g/255 <= 0.03928) ? ($g/255)/12.92 : ((($g/255) + 0.055)/1.055) ^ 2.4 }")
b=$(awk "BEGIN { print ($b/255 <= 0.03928) ? ($b/255)/12.92 : ((($b/255) + 0.055)/1.055) ^ 2.4 }")
echo $(awk "BEGIN { print (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b) }")
}
# Function to calculate contrast ratio
get_contrast_ratio() {
local lum1=$(get_luminance "$1")
local lum2=$(get_luminance "$2")
if [[ -z "$lum1" || -z "$lum2" ]]; then
echo 0
return
fi
if (( $(awk "BEGIN { print ($lum1 > $lum2) ? 1 : 0 }") )); then
awk "BEGIN { printf \"%.5f\", ($lum1 + 0.05) / ($lum2 + 0.05) }"
else
awk "BEGIN { printf \"%.5f\", ($lum2 + 0.05) / ($lum1 + 0.05) }"
fi
}
# Function to extract hex color
extract_color() {
local input="$1"
local color=""
if [[ "$input" =~ "#[0-9a-fA-F]{6}" ]]; then
color=$(echo "$input" | grep -o "#[0-9a-fA-F]\{6\}")
elif [[ "$input" =~ "1px solid #" ]]; then
color=$(echo "$input" | sed -E 's/.*1px solid (#[0-9a-fA-F]{6}).*/\1/')
elif [[ "$input" =~ "solid #" ]]; then
color=$(echo "$input" | sed -E 's/.*solid (#[0-9a-fA-F]{6}).*/\1/')
elif [[ "$input" =~ "#" ]]; then
color=$(echo "$input" | grep -o "#[0-9a-fA-F]*" | head -1)
fi
# Return normalized (lowercase) hex color
normalize_color "$color"
}
# Check contrast
check_contrast() {
local text_color="$1"
local background_color="$2"
local context="$3"
local border_color="$4"
local recommend_stroke="$5"
local is_background_check="$6"
local button_name="$7"
local check_failed=0
# Normalize all colors to lowercase for comparison
text_color=$(normalize_color "$text_color")
background_color=$(normalize_color "$background_color")
border_color=$(normalize_color "$border_color")
recommend_stroke=$(normalize_color "$recommend_stroke")
if [[ -z "$text_color" || -z "$background_color" ]]; then
return 0
fi
local contrast_ratio=$(get_contrast_ratio "$text_color" "$background_color")
if [[ -z "$contrast_ratio" ]]; then
contrast_ratio=0
fi
contrast_ratio=$(printf "%.2f" "$contrast_ratio")
# Case-insensitive comparison for hex colors
if (( $(awk "BEGIN { print ($contrast_ratio < $MIN_CONTRAST) ? 1 : 0 }") )); then
if [[ -n "$border_color" && "$border_color" == "$recommend_stroke" && "$is_background_check" -eq 1 ]]; then
echo "✅ [$context → $button_name] Contrast ratio $contrast_ratio fails WCAG but has a $recommend_stroke border → Treated as passing."
echo "✅ [$context → $button_name] Issue resolved by stroke → Fully passing."
check_failed=0
else
echo "❌ [$context → $button_name] Contrast ratio $contrast_ratio fails WCAG — Recommend adding a $recommend_stroke stroke."
check_failed=1
fi
else
echo "✅ [$context → $button_name] Contrast ratio $contrast_ratio passes WCAG"
check_failed=0
fi
return $check_failed
}
# For each button class, check its properties
for button_class in $BUTTON_CLASSES; do
echo "Checking button: $button_class"
# Extract button section
# Avoid partial matches
button_start=$(grep -n "\.button-$button_class\( \|{\)" css/brands.css | cut -d: -f1)
if [[ -z "$button_start" ]]; then
button_start=$(grep -n "\.button-$button_class$" css/brands.css | cut -d: -f1)
fi
if [[ -z "$button_start" ]]; then
echo "Could not find button-$button_class in css/brands.css"
continue
fi
# Look for the next closing bracket
button_end=$(tail -n +$button_start css/brands.css | grep -n "}" | head -1 | cut -d: -f1)
if [[ -z "$button_end" ]]; then
button_end=10
fi
button_section=$(tail -n +$button_start css/brands.css | head -n $button_end)
# Check for gradient
if echo "$button_section" | grep -q "background-image"; then
echo "🚩 [./css/brands.css → $button_class] Detected gradient background → Flagging for manual review."
NEEDS_MANUAL_REVIEW=1
continue
fi
# Extract colors
text_color=$(echo "$button_section" | grep "button-text" | grep -o "#[0-9A-Fa-f]*")
bg_color=$(echo "$button_section" | grep "button-background" | grep -o "#[0-9A-Fa-f]*")
border_color=$(extract_color "$(echo "$button_section" | grep "button-border")")
button_failed=0
# Check text vs background
if [[ -n "$text_color" && -n "$bg_color" ]]; then
check_contrast "$text_color" "$bg_color" "TEXT vs BUTTON" "$border_color" "" 0 "$button_class"
button_failed=$((button_failed | $?))
fi
# Check button vs light theme
if [[ -n "$bg_color" ]]; then
check_contrast "#ffffff" "$bg_color" "BUTTON vs LIGHT THEME" "$border_color" "#000000" 1 "$button_class"
button_failed=$((button_failed | $?))
# Check button vs dark theme
check_contrast "#121212" "$bg_color" "BUTTON vs DARK THEME" "$border_color" "#ffffff" 1 "$button_class"
button_failed=$((button_failed | $?))
fi
if [[ $button_failed -eq 1 ]]; then
FAILED=1
ALL_RESOLVED=0
fi
done
# Final report
if [[ "$NEEDS_MANUAL_REVIEW" -eq 1 ]]; then
echo "⚠️ Manual review required for gradients!"
exit 1
elif [[ "$ALL_RESOLVED" -eq 1 ]]; then
echo "✅ All contrast checks passed!"
exit 0
else
echo "❌ Contrast issues found!"
exit 1
fi
EOF
chmod +x contrast-check.sh
./contrast-check.sh
env:
GITHUB_BASE_REF: ${{ github.base_ref }}

View File

@@ -3,6 +3,10 @@
# LittleLink
The DIY self-hosted LinkTree alternative. LittleLink has more than 100 branded button styles you can easily use, with more regularly added by our community in this repo and in [LittleLink Extended](https://github.com/sethcottle/littlelink-extended).
---
### 🆕 LittleLink Button Builder
Want to make your own buttons for LittleLink but you're not too sure where to start? [Check out our new Button Builder](https://builder.littlelink.io). This new builder lets you preview button styles and with a single click, copy the generated CSS code to put in `css/brands.css`, and copy the generated HTML code to put in `index.html`. This builder also helps automate accessibility features by checking contrast ratios and suggesting strokes when needed, ensuring your custom buttons maintain LittleLink's high standards for visibility in both light and dark themes. Design your buttons visually, preview them live, and get ready to go code. [Live Site](https://builder.littlelink.io) | [GitHub Repo](https://github.com/sethcottle/littlelink-button-builder)
---
### 🌞 Themes and Accessibility
LittleLink offers `auto`, `light`, and `dark` themes right out of the box. If the default color schemes don't match your preference, you can easily customize them by updating the values in `style.css`. You can set any of the themes right in `index.html`. To enhance visual accessibility in both `light` and `dark` modes, buttons lacking sufficient contrast with the background are outlined with an inverse stroke to ensure visibility. While LittleLink emphasizes accessibility, it's important to acknowledge that not all brands incorporated into LittleLink achieve this standard. Branded buttons that come into LittleLink always retain the original essence of the brand and some branded buttons might fall short of optimal accessibility in terms of contrast.
@@ -61,19 +65,21 @@ Duplicate the [LittleLink Template on Figma Community](https://www.figma.com/com
### 💖 Supporters
You can support LittleLink by [buying me a coffee](https://www.buymeacoffee.com/seth). You can also have your name or your company added to this section and the supporters page of the [LittleLink.io](https://littlelink.io) website.
#### 🏢 Business Supporters
#### 🏢 Business Supporters ($75 tier)
**[links.dev](https://github.com/fatih-yavuz/links.dev)**
[![Add Your Company Name](https://cdn.cottle.cloud/littlelink/button-buy-me-a-coffee-company.svg)](https://www.buymeacoffee.com/seth/e/50574)
#### ✨ Individual Supporters
#### ✨ Individual Supporters ($25 tier)
**[Drew Davis](https://connect.davisdre.me)**
**[Robotter112](https://robotter112.de/)**
[![Buy Me A Coffee](https://cdn.cottle.cloud/littlelink/button-buy-me-a-coffee-individual.svg)](https://www.buymeacoffee.com/seth/e/50573)
#### 🐙 GitHub Sponsors
#### 🐙 Active GitHub Sponsors
**[@nghialele](https://github.com/nghialele)**
**[Your Name Here](https://github.com/sponsors/sethcottle)**
[![GitHub Sponsors](https://cdn.cottle.cloud/littlelink/button-github-sponsors.svg)](https://github.com/sponsors/sethcottle)
@@ -93,6 +99,14 @@ You can support LittleLink by [buying me a coffee](https://www.buymeacoffee.com/
---
### 🆕 Stay Connected
Join the [Seth's Nook Discord](https://discord.gg/PrAEQFF2fK) server to get updates on LittleLink and more. Use the invite code `PrAEQFF2fK` or click the button below.
[![Discord](https://cdn.cottle.cloud/littlelink/button-discord.svg)](https://discord.gg/PrAEQFF2fK)
---
### 📊 Analytics
To help build a more privacy focused product, we recommend using [Fathom Analytics](https://usefathom.com/ref/EQVZMV)*. [View our Fathom analytics dashboard](https://app.usefathom.com/share/xbmnwxxl/littlelink.io#/?filters=%5B%5D&range=last_7_days&site=2251799827005303)**.
@@ -102,3 +116,19 @@ To help build a more privacy focused product, we recommend using [Fathom Analyti
###### ** Analytics in this dashboard start May 03, 2022. View this [Google Sheets file](https://docs.google.com/spreadsheets/d/1GL4SroAdH-OZphBVR5z-BoSukHIEVJfao25q_e9-Ii8/edit?usp=sharing) with the generic unique pageview data from Google Analytics.
[![Fathom](https://cdn.cottle.cloud/littlelink/button-fathom-analytics.svg)](https://usefathom.com/ref/EQVZMV)
---
### 🐳 Docker Support
LittleLink includes Docker support for easy deployment and development. All Docker-related files are located in the `docker/` directory.
To run LittleLink using Docker:
```bash
docker compose -f docker/compose.yaml up
```
This will make the site available at http://localhost:8080
For more information about Docker configuration, see [docker/README.md](docker/README.md).
---

View File

@@ -1,6 +1,37 @@
# LittleLink Version History
## Current Version: v3.0.1
## Current Version: v3.6.0
### v3.6.0 - 3/25/2025
- Finally adds Docker support to LittleLink. See [PR #151](https://github.com/sethcottle/littlelink/pull/151), thank you [@lllahaye](https://github.com/lllahaye).
- Docker support has been a long-standing community request. While I previously closed similar PRs in an effort to keep the LittleLink repo as minimal as possible, several community forks emerged that added Docker support independently. Over the last few weeks I had been reconsidering this stance—this PR arrived at just the right time.
- Updated the brand color for Signal.
### v3.5.0 - 3/10/2025
- Added LittleLink Extended information in `index.html`
- Added `PULL_REQUEST_TEMPLATE.md` to `.github` which is a reflection of [submitting a new brand](https://github.com/sethcottle/littlelink/wiki/Submitting-a-new-brand-to-LittleLink) wiki.
### v3.4.0 - 3/04/2025
- Added Matrix
### v3.3.0 - 03/01/2025
- Updated Facebook Logo
- Updated Messenger Logo
- Updated Messenger Button Color
### v3.2.0 - 2/14/2025
- Added Apple Invites
- Removed Read.cv (service is winding down)
### v3.1.1 - 1/28/2025
- Fixed the alt text for Obsidian (`PR #138` / `@timtjtim`)
### v3.1.0 - 1/20/2025
- Added alternate YouTube button (`PR #138` / `@Omikorin`)
- Fixed `index.html` accessibilty issues (`PR #137` / `@rosahaj`)
### v3.0.2 - 12/20/2024
- Added Obsidian as a brand
### v3.0.1 - 11/13/2024
- Removed Trakt logo from `images/icons` since this now lives in LittleLink Extended

View File

@@ -58,6 +58,13 @@
--button-border:1px solid #FFFFFF;
}
/* Apple Invites */
.button-invites {
--button-text:#FFFFFF;
--button-background:#000000;
--button-border:1px solid #FFFFFF;
}
/* Apple Music */
.button-apple-music {
--button-text:#ffffff;
@@ -178,13 +185,13 @@
/* Facebook */
.button-faceb {
--button-text:#ffffff;
--button-background:#1877f2;
--button-background:#0866FF;
}
/* Facebook Messenger */
.button-messenger {
--button-text:#ffffff;
background-image:linear-gradient(25deg,#0099ff,#5f5dff,#a033ff,#c740cc,#ff5280,#ff7061);
--button-background:#0866FF;
}
/* Figma */
@@ -338,6 +345,13 @@
--button-border:1px solid #FFFFFF;
}
/* Matrix */
.button-matrix {
--button-text:#000000;
--button-background:#ffffff;
--button-border:1px solid #000000;
}
/* Medium */
.button-medium {
--button-text:#ffffff;
@@ -359,6 +373,13 @@
--button-border:1px solid #212121;
}
/* Obsidian */
.button.button-obsidian {
--button-text:#ffffff;
--button-background:#262626;
--button-border:1px solid #ffffff;
}
/* OnlyFans */
.button-onlyfans {
--button-text:#ffffff;
@@ -393,13 +414,6 @@
--button-border:1px solid #212121;
}
/* Read.cv */
.button-read-cv {
--button-text:#FFFFFF;
--button-background:#000000;
--button-border:1px solid #FFFFFF;
}
/* Reddit */
.button-reddit {
--button-text:#FFFFFF;
@@ -415,7 +429,14 @@
/* Signal */
.button-signal {
--button-text:#ffffff;
--button-background:#3a76f0;
--button-background:#3B45FD;
}
/* Signal Alt */
.button-signal-alt {
--button-text:#3B45FD;
--button-background:#E3E8FE;
--button-border:1px solid #000000;
}
/* Slack */
@@ -585,6 +606,12 @@
--button-border:1px solid #FFFFFF;
}
/* YouTube Alt */
.button-yt-alt {
--button-text:#ffffff;
--button-background:#FF0000;
}
/* Zoom */
.button-zoom {
--button-text:#ffffff;

46
docker/.dockerignore Normal file
View File

@@ -0,0 +1,46 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
# Git
.git
.github
.gitignore
# Docker
.dockerignore
docker-compose*
compose.yaml
# Logs
*.log
# Editor directories and files
.idea
.vscode
**/.DS_Store
# Documentation files
README*
LICENSE*
VERSION*
CONTRIBUTING*
# Exclude unnecessary files
**/__pycache__
**/.venv
**/bin
**/obj
**/charts
**/.env
**/secrets.dev.yaml
**/values.dev.yaml
# Keep only essential files for the static website
!index.html
!privacy.html
!css/
!images/
!fonts/

35
docker/Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM nginx:alpine
# Copy static website files
COPY . /usr/share/nginx/html/
# Configure nginx with basic optimization and logging to stdout/stderr
RUN echo 'server { \
listen 80; \
server_name localhost; \
root /usr/share/nginx/html; \
index index.html index.htm; \
\
# Enable access logging to stdout \
access_log /dev/stdout; \
error_log /dev/stderr; \
\
# Enable gzip compression \
gzip on; \
gzip_vary on; \
gzip_types text/plain text/css application/json application/javascript; \
\
# Basic cache settings \
location ~* \\.(?:css|js|jpg|jpeg|gif|png|ico|svg)$ { \
expires 7d; \
add_header Cache-Control "public"; \
} \
}' > /etc/nginx/conf.d/default.conf
# Forward nginx logs to Docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

231
docker/README.md Normal file
View File

@@ -0,0 +1,231 @@
# Docker for LittleLink
Docker configuration to run LittleLink in a container.
## File Structure
- `Dockerfile`: Defines how the image is built using nginx:alpine
- `compose.yaml`: Configuration for Docker Compose with volumes for development
- `.dockerignore`: Excludes unnecessary files from the image
## Technical Details
### Base Image
- Uses `nginx:alpine` for minimal image size (~20MB)
- Includes gzip compression for static files
- Optimized cache configuration for CSS, JavaScript, and images
- Configured to forward nginx logs to Docker log collector
### Volumes and Ports
- Mounts the project root directory as a volume for live development
- Exposes port 80 in the container, mapped to 8080 on the host
## Common Use Cases
### Creating Personal Link Pages for Different People
One of the main advantages of this Docker setup is how easily you can create multiple personalized instances of LittleLink:
```bash
# Clone the repository
git clone https://github.com/sethcottle/littlelink.git littlelink-john
# Customize the content for John
cd littlelink-john
# Edit index.html with John's links, customize images, etc.
# Build a Docker image for John's page
docker build -f docker/Dockerfile -t littlelink-john .
# Run John's page on port 8080
docker run -d --name john-links -p 8080:80 littlelink-john
```
For additional pages:
```bash
# Similarly for another person
git clone https://github.com/sethcottle/littlelink.git littlelink-jane
cd littlelink-jane
# Customize for Jane...
# Build and run on a different port
docker build -f docker/Dockerfile -t littlelink-jane .
docker run -d --name jane-links -p 8081:80 littlelink-jane
```
This approach allows you to:
- Maintain separate customized sites for different people
- Run multiple instances on different ports
- Update each site independently
- Easily deploy to various environments
## Development vs. Production
There are two main ways to use Docker with LittleLink:
### Development Workflow
In development, we use Docker Compose with mounted volumes to allow for live editing:
```bash
# Start development environment
docker compose -f docker/compose.yaml up
```
This configuration:
- Mounts local files as a volume, so changes are reflected immediately
- Requires manual browser refresh to see changes
- Is ideal for testing and development
### Production Workflow
For production, you have two options:
#### Option 1: Production with Docker Compose
Create a production-specific docker-compose file:
```yaml
# docker/compose.prod.yaml
services:
web:
image: yourname/littlelink:latest
restart: always
ports:
- "8080:80"
# Optional volume for customizable content
volumes:
- /path/on/server/custom-content:/usr/share/nginx/html
```
Deploy using:
```bash
# Build and tag the image
docker build -f docker/Dockerfile -t yourname/littlelink:latest .
# Run in production with compose
docker compose -f docker/compose.prod.yaml up -d
```
#### Option 2: Production with Docker Run
```bash
# Build a production image
docker build -f docker/Dockerfile -t yourname/littlelink:latest .
# Run in production (no volumes mounted)
docker run -d --name littlelink -p 80:80 --restart always yourname/littlelink:latest
```
## Using Volumes in Production
You can customize the content in production by mounting a local directory:
```bash
# Prepare a directory with your custom content
mkdir -p /path/on/server/custom-content
cp -r index.html css/ images/ /path/on/server/custom-content/
# Run with the custom content mounted
docker run -d --name littlelink -p 80:80 \
-v /path/on/server/custom-content:/usr/share/nginx/html \
yourname/littlelink:latest
```
With Docker Compose:
```yaml
services:
web:
image: yourname/littlelink:latest
ports:
- "80:80"
volumes:
- /path/on/server/custom-content:/usr/share/nginx/html
```
This approach:
- Allows content customization without rebuilding the image
- Makes it easy to update content independently of the container
## Docker Commands Reference
### Development Commands
```bash
# Start in development mode
docker compose -f docker/compose.yaml up
# Start in background
docker compose -f docker/compose.yaml up -d
# Stop container
docker compose -f docker/compose.yaml down
# View logs (including HTTP request logs)
docker compose -f docker/compose.yaml logs -f
```
### Production Commands
```bash
# Build production image
docker build -f docker/Dockerfile -t yourname/littlelink:latest .
# Run production container
docker run -d --name littlelink -p 80:80 yourname/littlelink:latest
# View logs for the running container
docker logs -f littlelink
```
## Customization
### Change Port
Edit `docker/compose.yaml` for development:
```yaml
ports:
- "8081:80" # Change 8080 to desired port
```
Or specify port when running production container:
```bash
docker run -p 8081:80 yourname/littlelink:latest
```
### Additional nginx Configuration
To modify the nginx configuration, you can edit the `Dockerfile` and add your own configuration:
```dockerfile
# Example: add custom configuration
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
```
## Deploying to Production
### Docker on VPS
```bash
# Pull image
docker pull yourname/littlelink:latest
# Run container
docker run -d --name littlelink -p 80:80 yourname/littlelink:latest
# With restart policy for auto-recovery
docker run -d --name littlelink --restart unless-stopped -p 80:80 yourname/littlelink:latest
```
### Multiple Sites on One Server
You can run multiple LittleLink instances on the same server:
```bash
# Run first site on port 8080
docker run -d --name site1 -p 8080:80 littlelink-site1
# Run second site on port 8081
docker run -d --name site2 -p 8081:80 littlelink-site2
```

10
docker/compose.yaml Normal file
View File

@@ -0,0 +1,10 @@
services:
web:
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "8080:80"
volumes:
- ..:/usr/share/nginx/html
restart: unless-stopped

View File

@@ -0,0 +1 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="12" x2="12" y1=".5" y2="8.5"><stop offset="0" stop-color="#fecb00"/><stop offset="1" stop-color="#f3c200"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="12" x2="12" y1="4.93463" y2="24"><stop offset="0" stop-color="#fedf00"/><stop offset="1" stop-color="#ffce00"/></linearGradient><clipPath id="c"><path d="m0 0h24v24h-24z"/></clipPath><g clip-path="url(#c)"><rect fill="#fff" height="17" rx="6" width="24"/><path d="m5.10545 9.1754c.86078 0 1.55858-.6978 1.55858-1.55857 0-.86078-.6978-1.55858-1.55858-1.55858s-1.55857.6978-1.55857 1.55858c0 .86077.69779 1.55857 1.55857 1.55857z" fill="#ff284c"/><path d="m3.52537 10.8893c.86078 0 1.55858-.6978 1.55858-1.55855 0-.86078-.6978-1.55858-1.55858-1.55858s-1.55857.6978-1.55857 1.55858c0 .86075.69779 1.55855 1.55857 1.55855z" fill="#00cbf4" opacity=".9"/><path d="m12.8922 14.2188c.8951 0 1.6207-.7256 1.6207-1.6207s-.7256-1.6208-1.6207-1.6208-1.6207.7257-1.6207 1.6208.7256 1.6207 1.6207 1.6207z" fill="#00c3f3"/><path d="m14.6068 16.4557c.9208 0 1.6673-.7465 1.6673-1.6674 0-.9208-.7465-1.6673-1.6673-1.6673-.9209 0-1.6673.7465-1.6673 1.6673 0 .9209.7464 1.6674 1.6673 1.6674z" fill="#00a102" opacity=".9"/><path d="m17.0618 13.0796c.9209 0 1.6674-.7465 1.6674-1.6673s-.7465-1.66731-1.6674-1.66731c-.9208 0-1.6673.74651-1.6673 1.66731s.7465 1.6673 1.6673 1.6673z" fill="#ff8600"/><path d="m20.2611 10.9722c.9208 0 1.6673-.7465 1.6673-1.66734s-.7465-1.66732-1.6673-1.66732c-.9209 0-1.6673.74648-1.6673 1.66732s.7464 1.66734 1.6673 1.66734z" fill="#ff284c"/><path d="m16.4447 9.52751c.9208 0 1.6673-.74648 1.6673-1.66732 0-.92083-.7465-1.66731-1.6673-1.66731-.9209 0-1.6674.74648-1.6674 1.66731 0 .92084.7465 1.66732 1.6674 1.66732z" fill="#ffc100"/><path d="m14.1203 10.9722c.8722 0 1.5793-.7071 1.5793-1.57932s-.7071-1.57929-1.5793-1.57929-1.5793.70707-1.5793 1.57929.7071 1.57932 1.5793 1.57932z" fill="#ad42cf" opacity=".9"/><path d="m13.748 5.71133c.8608 0 1.5586-.6978 1.5586-1.55858 0-.86077-.6978-1.55857-1.5586-1.55857-.8607 0-1.5585.6978-1.5585 1.55857 0 .86078.6978 1.55858 1.5585 1.55858z" fill="#00c3f3"/><path d="m8.52733 7.11457c.86077 0 1.55857-.6978 1.55857-1.55858s-.6978-1.55858-1.55857-1.55858c-.86078 0-1.55858.6978-1.55858 1.55858s.6978 1.55858 1.55858 1.55858z" fill="#ffc100"/><path d="m9.62498 10.6667c.86082 0 1.55862-.69783 1.55862-1.55861s-.6978-1.55858-1.55862-1.55858c-.86078 0-1.55857.6978-1.55857 1.55858s.69779 1.55861 1.55857 1.55861z" fill="#ff8600"/><path d="m9.75584 13.1262c.86076 0 1.55856-.6978 1.55856-1.5586 0-.8607-.6978-1.5585-1.55856-1.5585-.86078 0-1.55857.6978-1.55857 1.5585 0 .8608.69779 1.5586 1.55857 1.5586z" fill="#ffc100" opacity=".9"/><path d="m6.46678 12.6395c.86078 0 1.55857-.6978 1.55857-1.5586s-.69779-1.55857-1.55857-1.55857-1.55858.69777-1.55858 1.55857.6978 1.5586 1.55858 1.5586z" fill="#ad42cf"/><path d="m10.4121 16.4142c.8608 0 1.5586-.6978 1.5586-1.5585 0-.8608-.6978-1.5586-1.5586-1.5586-.86079 0-1.55858.6978-1.55858 1.5586 0 .8607.69779 1.5585 1.55858 1.5585z" fill="#ff59c2"/><path d="m23.9697 4.93981c-.1709.23301-.3573.45048-.5644.6576l-.901.90097v-.33139c0-2.57864-2.0919-4.67055-4.6705-4.67055h-11.67122c-2.57864 0-4.67055 2.09191-4.67055 4.67055v.37799l-.947568-.94757c-.191586-.19159-.367638-.39353-.5229776-.60582.2381876-2.79612 2.5838156-4.99159 5.4420656-4.99159h13.07445c2.8375 0 5.1728 2.1644 5.4369 4.93463z" fill="url(#a)"/><path d="m23.9689 4.93981c-.1708.23301-.3572.45048-.5644.6576l-.9009.90098-8.0052 8.00521c-1.3929 1.3928-3.6557 1.3928-5.04856 0l-7.95858-7.95861-.947571-.94758c-.191585-.19158-.367638-.39352-.5229773-.60582-.01553402.15534-.0207117.31068-.0207117.4712v13.07441c0 3.0188 2.44401 5.4628 5.46278 5.4628h13.07442c3.0188 0 5.4628-2.444 5.4628-5.4628v-13.07441c0-.17606-.0104-.35211-.0259-.52816z" fill="url(#b)"/><path d="m12.3125 8c.8608 0 1.5586-.6978 1.5586-1.55857 0-.86078-.6978-1.55858-1.5586-1.55858s-1.5586.6978-1.5586 1.55858c0 .86077.6978 1.55857 1.5586 1.55857z" fill="#00a102" opacity=".9"/><path d="m16.7775 7.8602c.9094 0 1.6466-.73721 1.6466-1.6466 0-.9094-.7372-1.64661-1.6466-1.64661s-1.6466.73721-1.6466 1.64661c0 .90939.7372 1.6466 1.6466 1.6466z" fill="#ff59c2" opacity=".9"/></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1 +1,10 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h24v24h-24z"/></clipPath><g clip-path="url(#a)"><path d="m23.625 12c0-6.42188-5.2031-11.625-11.625-11.625-6.42188 0-11.625 5.20312-11.625 11.625 0 5.8022 4.25109 10.6116 9.8086 11.4844v-8.1239h-2.95313v-3.3605h2.95313v-2.56125c0-2.91328 1.7344-4.5225 4.3908-4.5225 1.2722 0 2.6025.22688 2.6025.22688v2.85937h-1.4663c-1.4437 0-1.8942.89625-1.8942 1.81547v2.18203h3.2241l-.5157 3.3605h-2.7084v8.1239c5.5575-.8728 9.8086-5.6822 9.8086-11.4844z" fill="#fff"/></g></svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4_34)">
<path d="M24 12C24 5.37264 18.6274 0 12 0C5.37264 0 0 5.37264 0 12C0 17.6275 3.87456 22.3498 9.10128 23.6467V15.6672H6.62688V12H9.10128V10.4198C9.10128 6.33552 10.9498 4.4424 14.9597 4.4424C15.72 4.4424 17.0318 4.59168 17.5685 4.74048V8.06448C17.2853 8.03472 16.7933 8.01984 16.1822 8.01984C14.2147 8.01984 13.4544 8.76528 13.4544 10.703V12H17.3741L16.7006 15.6672H13.4544V23.9122C19.3963 23.1946 24.0005 18.1354 24.0005 12H24Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_4_34">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 633 B

After

Width:  |  Height:  |  Size: 683 B

7
images/icons/matrix.svg Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 520 520" style="enable-background:new 0 0 520 520;" xml:space="preserve">
<path d="M13.7,11.9v496.2h35.7V520H0V0h49.4v11.9H13.7z"/>
<path d="M166.3,169.2v25.1h0.7c6.7-9.6,14.8-17,24.2-22.2c9.4-5.3,20.3-7.9,32.5-7.9c11.7,0,22.4,2.3,32.1,6.8 c9.7,4.5,17,12.6,22.1,24c5.5-8.1,13-15.3,22.4-21.5c9.4-6.2,20.6-9.3,33.5-9.3c9.8,0,18.9,1.2,27.3,3.6c8.4,2.4,15.5,6.2,21.5,11.5 c6,5.3,10.6,12.1,14,20.6c3.3,8.5,5,18.7,5,30.7v124.1h-50.9V249.6c0-6.2-0.2-12.1-0.7-17.6c-0.5-5.5-1.8-10.3-3.9-14.3 c-2.2-4.1-5.3-7.3-9.5-9.7c-4.2-2.4-9.9-3.6-17-3.6c-7.2,0-13,1.4-17.4,4.1c-4.4,2.8-7.9,6.3-10.4,10.8c-2.5,4.4-4.2,9.4-5,15.1 c-0.8,5.6-1.3,11.3-1.3,17v103.3h-50.9v-104c0-5.5-0.1-10.9-0.4-16.3c-0.2-5.4-1.3-10.3-3.1-14.9c-1.8-4.5-4.8-8.2-9-10.9 c-4.2-2.7-10.3-4.1-18.5-4.1c-2.4,0-5.6,0.5-9.5,1.6c-3.9,1.1-7.8,3.1-11.5,6.1c-3.7,3-6.9,7.3-9.5,12.9c-2.6,5.6-3.9,13-3.9,22.1 v107.6h-50.9V169.2H166.3z"/>
<path d="M506.3,508.1V11.9h-35.7V0H520v520h-49.4v-11.9H506.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +1,10 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h24v24h-24z"/></clipPath><g clip-path="url(#a)"><path clip-rule="evenodd" d="m0 11.64c0-6.68831 5.24005-11.64 12-11.64 6.76 0 12 4.95169 12 11.64 0 6.6884-5.24 11.64-12 11.64-1.2142 0-2.3791-.1603-3.47362-.4612-.21276-.0586-.43901-.042-.64093.047l-2.38183 1.0514c-.62304.2751-1.32634-.168-1.34722-.8488l-.06535-2.1346c-.00804-.263-.12636-.5088-.32237-.6842-2.33492-2.0881-3.76868-5.1111-3.76868-8.6096zm8.31906-2.1884-3.525 5.5925c-.33828.5367.32143 1.1415.8268.758l3.78644-2.8737c.25613-.1944.6101-.1954.8673-.0025l2.8037 2.1028c.8412.631 2.042.4094 2.6027-.4802l3.525-5.59255c.3382-.53671-.3214-1.14151-.8268-.75799l-3.7865 2.87364c-.2561.1944-.61.1954-.8673.0025l-2.8036-2.10274c-.8413-.63096-2.04202-.40944-2.60274.48024z" fill="#fff" fill-rule="evenodd"/></g></svg>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4_39)">
<path d="M11.9991 0.0478516C5.26616 0.0478516 0.046875 4.9798 0.046875 11.6415C0.046875 15.1263 1.47492 18.1368 3.80034 20.2169C3.99588 20.3914 4.11349 20.6367 4.12162 20.8982L4.18664 23.0247C4.20719 23.7027 4.90807 24.1439 5.52815 23.87L7.90042 22.8225C8.1017 22.7341 8.32688 22.7173 8.53867 22.7761C9.62919 23.0759 10.789 23.2356 11.9986 23.2356C18.7315 23.2356 23.9508 18.3036 23.9508 11.642C23.9508 4.98028 18.732 0.0478516 11.9991 0.0478516ZM19.4056 8.54777L15.2443 14.979C15.033 15.3055 14.5975 15.3988 14.2709 15.1875L10.4161 12.6933C10.267 12.5967 10.0738 12.5996 9.92751 12.7004L5.58265 15.6971C4.94871 16.1341 4.17373 15.382 4.59205 14.7357L8.75381 8.30442C8.96512 7.97789 9.40066 7.88466 9.72672 8.09598L13.5825 10.5906C13.7317 10.6872 13.9248 10.6843 14.0711 10.5835L18.415 7.58729C19.0489 7.14984 19.8239 7.90235 19.4056 8.54873V8.54777Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_4_39">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 1.1 KiB

51
images/icons/obsidian.svg Normal file
View File

@@ -0,0 +1,51 @@
<svg fill="none" height="100%" width="100%" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<radialGradient id="logo-bottom-left" cx="0" cy="0" gradientTransform="matrix(-59 -225 150 -39 161.4 470)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".4"/>
<stop offset="1" stop-opacity=".1"/>
</radialGradient>
<radialGradient id="logo-top-right" cx="0" cy="0" gradientTransform="matrix(50 -379 280 37 360 374.2)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".6"/>
<stop offset="1" stop-color="#fff" stop-opacity=".1"/>
</radialGradient>
<radialGradient id="logo-top-left" cx="0" cy="0" gradientTransform="matrix(69 -319 218 47 175.4 307)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".8"/>
<stop offset="1" stop-color="#fff" stop-opacity=".4"/>
</radialGradient>
<radialGradient id="logo-bottom-right" cx="0" cy="0" gradientTransform="matrix(-96 -163 187 -111 335.3 512.2)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".3"/>
<stop offset="1" stop-opacity=".3"/>
</radialGradient>
<radialGradient id="logo-top-edge" cx="0" cy="0" gradientTransform="matrix(-36 166 -112 -24 310 128.2)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity="0"/>
<stop offset="1" stop-color="#fff" stop-opacity=".2"/>
</radialGradient>
<radialGradient id="logo-left-edge" cx="0" cy="0" gradientTransform="matrix(88 89 -190 187 111 220.2)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".2"/>
<stop offset="1" stop-color="#fff" stop-opacity=".4"/>
</radialGradient>
<radialGradient id="logo-bottom-edge" cx="0" cy="0" gradientTransform="matrix(9 130 -276 20 215 284)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".2"/>
<stop offset="1" stop-color="#fff" stop-opacity=".3"/>
</radialGradient>
<radialGradient id="logo-middle-edge" cx="0" cy="0" gradientTransform="matrix(-198 -104 327 -623 400 399.2)" gradientUnits="userSpaceOnUse" r="1">
<stop offset="0" stop-color="#fff" stop-opacity=".2"/>
<stop offset=".5" stop-color="#fff" stop-opacity=".2"/>
<stop offset="1" stop-color="#fff" stop-opacity=".3"/>
</radialGradient>
<clipPath id="clip">
<path d="M.2.2h512v512H.2z"/>
</clipPath>
<g clip-path="url(#clip)">
<path d="M382.3 475.6c-3.1 23.4-26 41.6-48.7 35.3-32.4-8.9-69.9-22.8-103.6-25.4l-51.7-4a34 34 0 0 1-22-10.2l-89-91.7a34 34 0 0 1-6.7-37.7s55-121 57.1-127.3c2-6.3 9.6-61.2 14-90.6 1.2-7.9 5-15 11-20.3L248 8.9a34.1 34.1 0 0 1 49.6 4.3L386 125.6a37 37 0 0 1 7.6 22.4c0 21.3 1.8 65 13.6 93.2 11.5 27.3 32.5 57 43.5 71.5a17.3 17.3 0 0 1 1.3 19.2 1494 1494 0 0 1-44.8 70.6c-15 22.3-21.9 49.9-25 73.1z" fill="#6c31e3"/>
<path d="M165.9 478.3c41.4-84 40.2-144.2 22.6-187-16.2-39.6-46.3-64.5-70-80-.6 2.3-1.3 4.4-2.2 6.5L60.6 342a34 34 0 0 0 6.6 37.7l89.1 91.7a34 34 0 0 0 9.6 7z" fill="url(#logo-bottom-left)"/>
<path d="M278.4 307.8c11.2 1.2 22.2 3.6 32.8 7.6 34 12.7 65 41.2 90.5 96.3 1.8-3.1 3.6-6.2 5.6-9.2a1536 1536 0 0 0 44.8-70.6 17 17 0 0 0-1.3-19.2c-11-14.6-32-44.2-43.5-71.5-11.8-28.2-13.5-72-13.6-93.2 0-8.1-2.6-16-7.6-22.4L297.6 13.2a34 34 0 0 0-1.5-1.7 96 96 0 0 1 2 54 198.3 198.3 0 0 1-17.6 41.3l-7.2 14.2a171 171 0 0 0-19.4 71c-1.2 29.4 4.8 66.4 24.5 115.8z" fill="url(#logo-top-right)"/>
<path d="M278.4 307.8c-19.7-49.4-25.8-86.4-24.5-115.9a171 171 0 0 1 19.4-71c2.3-4.8 4.8-9.5 7.2-14.1 7.1-13.9 14-27 17.6-41.4a96 96 0 0 0-2-54A34.1 34.1 0 0 0 248 9l-105.4 94.8a34.1 34.1 0 0 0-10.9 20.3l-12.8 85-.5 2.3c23.8 15.5 54 40.4 70.1 80a147 147 0 0 1 7.8 24.8c28-6.8 55.7-11 82.1-8.3z" fill="url(#logo-top-left)"/>
<path d="M333.6 511c22.7 6.2 45.6-12 48.7-35.4a187 187 0 0 1 19.4-63.9c-25.6-55-56.5-83.6-90.4-96.3-36-13.4-75.2-9-115 .7 8.9 40.4 3.6 93.3-30.4 162.2 4 1.8 8.1 3 12.5 3.3 0 0 24.4 2 53.6 4.1 29 2 72.4 17.1 101.6 25.2z" fill="url(#logo-bottom-right)"/>
<g clip-rule="evenodd" fill-rule="evenodd">
<path d="M254.1 190c-1.3 29.2 2.4 62.8 22.1 112.1l-6.2-.5c-17.7-51.5-21.5-78-20.2-107.6a174.7 174.7 0 0 1 20.4-72c2.4-4.9 8-14.1 10.5-18.8 7.1-13.7 11.9-21 16-33.6 5.7-17.5 4.5-25.9 3.8-34.1 4.6 29.9-12.7 56-25.7 82.4a177.1 177.1 0 0 0-20.7 72z" fill="url(#logo-top-edge)"/>
<path d="M194.3 293.4c2.4 5.4 4.6 9.8 6 16.5L195 311c-2.1-7.8-3.8-13.4-6.8-20-17.8-42-46.3-63.6-69.7-79.5 28.2 15.2 57.2 39 75.7 81.9z" fill="url(#logo-left-edge)"/>
<path d="M200.6 315.1c9.8 46-1.2 104.2-33.6 160.9 27.1-56.2 40.2-110.1 29.3-160z" fill="url(#logo-bottom-edge)"/>
<path d="M312.5 311c53.1 19.9 73.6 63.6 88.9 100-19-38.1-45.2-80.3-90.8-96-34.8-11.8-64.1-10.4-114.3 1l-1.1-5c53.2-12.1 81-13.5 117.3 0z" fill="url(#logo-middle-edge)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -1 +0,0 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h24v24h-24z"/></clipPath><g clip-path="url(#a)"><path clip-rule="evenodd" d="m7.81661.0783257 13.47389 3.3594043c1.4019.34953 2.255 1.76934 1.9054 3.17123l-3.8414 15.40724c-.3496 1.4019-1.7694 2.255-3.1713 1.9055l-13.47382-3.3594c-1.40189-.3496-2.255-1.7694-1.905468-3.1713l3.841458-15.4072c.34953-1.401898 1.76934-2.255005 3.17124-1.9054743zm-.54126 4.3583943 12.51095 3.11932c.2835.0707.4561.35791.3854.6415l-.1659.66528c-.0707.28358-.3579.45616-.6415.38545l-12.51088-3.11932c-.28358-.0707-.45616-.35791-.38545-.6415l.16587-.66528c.07071-.28358.35792-.45616.64151-.38545zm11.55995 7.07398-12.51095-3.1193c-.28359-.0707-.5708.10187-.64151.38546l-.16587.66528c-.07071.28358.10187.57076.38546.64146l12.51087 3.1194c.2836.0707.5708-.1019.6415-.3855l.1659-.6653c.0707-.2836-.1019-.5708-.3854-.6415zm-13.49406.5851 5.74196 1.4317c.2836.0707.4562.3579.3855.6415l-.1659.6653c-.0707.2835-.3579.4561-.6415.3854l-5.74198-1.4316c-.28359-.0707-.45616-.3579-.38546-.6415l.16587-.6653c.07071-.2836.35792-.4562.64151-.3855z" fill="#fff" fill-rule="evenodd"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,26 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_25_65)">
<path d="M12 0C12.6226 0 13.2341 0.0474105 13.8311 0.138818L13.6594 1.2508C13.1184 1.16797 12.5642 1.125 12 1.125C11.4358 1.125 10.8817 1.16796 10.3407 1.25079L10.169 0.138806C10.766 0.0474064 11.3775 0 12 0Z" fill="#3B45FD"/>
<path d="M14.8477 0.339844L14.5808 1.43298C15.6772 1.69982 16.7085 2.13289 17.6466 2.70394L18.2308 1.74229C17.1957 1.11215 16.0576 0.634298 14.8477 0.339844Z" fill="#3B45FD"/>
<path d="M19.0918 2.31885L18.427 3.22645C19.3236 3.88437 20.1156 4.67641 20.7735 5.57301L21.6811 4.90815C20.9551 3.91879 20.0812 3.04482 19.0918 2.31885Z" fill="#3B45FD"/>
<path d="M22.2576 5.76953L21.296 6.35366C21.867 7.29178 22.3001 8.32315 22.5669 9.41959L23.6601 9.15262C23.3656 7.94276 22.8878 6.8047 22.2576 5.76953Z" fill="#3B45FD"/>
<path d="M23.8612 10.1689L22.7491 10.3406C22.8321 10.8816 22.875 11.4358 22.875 11.9999C22.875 12.5641 22.8321 13.1183 22.7491 13.6594L23.8612 13.831C23.9526 13.234 24 12.6225 24 11.9999C24 11.3774 23.9526 10.7659 23.8612 10.1689Z" fill="#3B45FD"/>
<path d="M21.296 17.6466C21.867 16.7083 22.3001 15.6771 22.5669 14.5806L23.6601 14.8475C23.3656 16.0575 22.8878 17.1955 22.2576 18.2307L21.296 17.6466Z" fill="#3B45FD"/>
<path d="M20.7735 18.4272L21.6812 19.092C20.9552 20.0814 20.0813 20.9553 19.0919 21.6813L18.4269 20.7737C19.3236 20.1158 20.1156 19.3238 20.7735 18.4272Z" fill="#3B45FD"/>
<path d="M17.6466 21.2959L18.2307 22.2575C17.1956 22.8877 16.0575 23.3656 14.8476 23.66L14.5807 22.5668C15.6771 22.3 16.7085 21.8669 17.6466 21.2959Z" fill="#3B45FD"/>
<path d="M13.6593 22.749L13.831 23.8611C13.234 23.9525 12.6225 23.9999 12 23.9999C11.3774 23.9999 10.7659 23.9525 10.1689 23.8611L10.3405 22.7492C10.8813 22.832 11.4355 22.8749 12 22.8749C12.5642 22.8749 13.1183 22.832 13.6593 22.749Z" fill="#3B45FD"/>
<path d="M9.41917 22.5672L9.15226 23.6601C8.24343 23.4388 7.37511 23.1142 6.56049 22.6992L5.42159 22.965L5.16596 21.8694L6.70501 21.5103L7.07113 21.6967C7.80894 22.0726 8.59548 22.3668 9.41917 22.5672Z" fill="#3B45FD"/>
<path d="M4.21646 22.0909L4.47209 23.1865L2.51991 23.6419C1.22079 23.945 0.0548528 22.7791 0.357982 21.4801L0.813491 19.5278L1.90907 19.7834L1.45355 21.7357C1.33988 22.2229 1.77711 22.66 2.26428 22.5464L4.21646 22.0909Z" fill="#3B45FD"/>
<path d="M2.13062 18.8338L1.03504 18.5782L1.30079 17.4393C0.885789 16.6246 0.561101 15.7563 0.339908 14.8475L1.43279 14.5806C1.63319 15.4042 1.92735 16.1908 2.30321 16.9287L2.48973 17.2948L2.13062 18.8338Z" fill="#3B45FD"/>
<path d="M1.25064 13.6594L0.138804 13.831C0.0474057 13.234 0 12.6226 0 12C0 11.3775 0.04741 10.766 0.138817 10.1689L1.2508 10.3406C1.16797 10.8817 1.125 11.4358 1.125 12C1.125 12.5645 1.16792 13.1186 1.25064 13.6594Z" fill="#3B45FD"/>
<path d="M1.43306 9.4191L0.339927 9.15213C0.63438 7.94227 1.11223 6.80421 1.74237 5.76904L2.70402 6.35317C2.13297 7.29129 1.69991 8.32266 1.43306 9.4191Z" fill="#3B45FD"/>
<path d="M3.22647 5.57299L2.31886 4.90813C3.04483 3.91878 3.91881 3.04482 4.90817 2.31885L5.57303 3.22645C4.67643 3.88437 3.88438 4.6764 3.22647 5.57299Z" fill="#3B45FD"/>
<path d="M6.3534 2.70392L5.76926 1.74226C6.80442 1.11214 7.94249 0.634292 9.15236 0.339844L9.41933 1.43298C8.32289 1.69982 7.29151 2.13286 6.3534 2.70392Z" fill="#3B45FD"/>
<path d="M21.75 12C21.75 17.3849 17.3849 21.75 12 21.75C10.292 21.75 8.68661 21.3108 7.29053 20.5392C7.15611 20.4649 6.99927 20.4405 6.84969 20.4753L2.51268 21.4873L3.52466 17.1503C3.55955 17.0007 3.53503 16.8438 3.46075 16.7094C2.68917 15.3134 2.25 13.708 2.25 12C2.25 6.61523 6.61523 2.25 12 2.25C17.3849 2.25 21.75 6.61523 21.75 12Z" fill="#3B45FD"/>
</g>
<defs>
<clipPath id="clip0_25_65">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1 @@
<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><defs><mask id="a" x="0" y="0" width="24" height="24"><path fill="#fff" d="M0 0h24v24H0z"/><path d="M9.545 15.588 15.818 12 9.545 8.412z" fill="#000"/></mask></defs><path d="M23.498 6.154a3.02 3.02 0 0 0-2.122-2.147C19.506 3.5 12 3.5 12 3.5s-7.505 0-9.376.507A3.02 3.02 0 0 0 .502 6.154C0 8.05 0 12 0 12s0 3.951.502 5.846a3.02 3.02 0 0 0 2.122 2.147C4.494 20.5 12 20.5 12 20.5s7.506 0 9.376-.507a3.02 3.02 0 0 0 2.122-2.147C24 15.95 24 12 24 12s0-3.951-.502-5.846" fill="#fff" mask="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 594 B

View File

@@ -34,6 +34,11 @@
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/brands.css">
<!-- LittleLink Extended Stylesheet (Optional) -->
<!-- <link rel="stylesheet" href="css/brands-extended.css"> -->
<!-- Learn more at https://github.com/sethcottle/littlelink-extended.
Remove comments if you've added LittleLink Extended dependencies -->
</head>
<body>
@@ -52,12 +57,12 @@
<img class="avatar avatar--rounded" src="images/avatar.png" srcset="images/avatar@2x.png 2x" alt="LittleLink">
<!-- Replace with your name or brand -->
<h1 tabindex="0">
<h1>
<div>LittleLink</div>
</h1>
<!-- Add a short description about yourself or your brand -->
<p tabindex="0">An open source DIY Linktree alternative.</p>
<p>An open source DIY Linktree alternative.</p>
<!-- All your buttons go here -->
<div class="button-stack" role="navigation">
@@ -74,6 +79,9 @@
<!-- Apple App Store -->
<a class="button button-appstore" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/apple.svg" alt="Apple Logo">Apple App Store</a>
<!-- Apple Invites -->
<a class="button button-invites" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/apple-invites.svg" alt="Apple Invites Logo">Apple Invites</a>
<!-- Apple Music -->
<a class="button button-apple-music" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/apple-music.svg" alt="Apple Music Logo">Listen on Apple Music</a>
@@ -203,6 +211,9 @@
<!-- Mastodon -->
<a class="button button-mastodon" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/mastodon.svg" alt="Mastodon Logo">Mastodon</a>
<!-- Matrix -->
<a class="button button-matrix" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/matrix.svg" alt="Matrix Logo">Matrix</a>
<!-- Medium -->
<a class="button button-medium" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/medium.svg" alt="Medium Logo">Medium</a>
@@ -212,6 +223,9 @@
<!-- Notion -->
<a class="button button-notion" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/notion.svg" alt="Notion Logo">Notion</a>
<!-- Obsidian -->
<a class="button button-obsidian" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/obsidian.svg" alt="Obsidian Logo">Obsidian</a>
<!-- OnlyFans -->
<a class="button button-onlyfans" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/onlyfans.svg" alt="OnlyFans Logo">OnlyFans (18+)</a>
@@ -227,9 +241,6 @@
<!-- Product Hunt -->
<a class="button button-product-hunt" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/product-hunt.svg" alt="Product Hunt Logo">Product Hunt</a>
<!-- Read.cv -->
<a class="button button-read-cv" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/read-cv.svg" alt="Read.cv logo">Read.cv</a>
<!-- Reddit -->
<a class="button button-reddit" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/reddit.svg" alt="Reddit Logo">Reddit</a>
@@ -239,6 +250,9 @@
<!-- Signal -->
<a class="button button-signal" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/signal.svg" alt="Signal Logo">Signal</a>
<!-- Signal Alt -->
<a class="button button-signal-alt" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/signal-alt.svg" alt="Signal Logo">Signal</a>
<!-- Slack -->
<a class="button button-slack" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/slack.svg" alt="Slack Logo">Join Slack</a>
@@ -317,6 +331,9 @@
<!-- YouTube -->
<a class="button button-yt" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/youtube.svg" alt="YouTube Logo">YouTube</a>
<!-- YouTube Alt -->
<a class="button button-yt-alt" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/youtube-alt.svg" alt="YouTube Logo">YouTube</a>
<!-- YouTube Music -->
<a class="button button-yt" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/youtube-music.svg" alt="YouTube Music Logo">Listen on YouTube Music</a>
@@ -371,6 +388,9 @@
<!-- Generic Website -->
<a class="button button-default" href="#" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/generic-website.svg" alt="Website Icon">Visit Website</a>
<!-- LittleLink Extended -->
<a class="button button-default" href="https://github.com/sethcottle/littlelink-extended" target="_blank" rel="noopener" role="button"><img class="icon" aria-hidden="true" src="images/icons/littlelink.svg" alt="LittleLink Logo">LittleLink Extended</a>
</div>
<!-- Feel free to add your own footer information, including updating `privacy.html` to reflect how your LittleLink fork is set up -->

View File

@@ -32,43 +32,43 @@
<body>
<div class="container-left" role="main">
<div class="column">
<nav role="navigation" aria-label="Back to homepage">
<a href="index.html" tabindex="0">← Back to main page</a>
<nav role="navigation">
<a href="index.html" aria-label="Back to homepage">← Back to main page</a>
</nav>
<h1 tabindex="0">Privacy Overview</h1>
<h1>Privacy Overview</h1>
<section aria-labelledby="analytics-heading">
<h2 id="analytics-heading" tabindex="0">Analytics</h2>
<p tabindex="0">The services contained in this section enable the Owner to monitor and analyze web traffic and can be used to keep track of User behavior.</p>
<h2 id="analytics-heading">Analytics</h2>
<p>The services contained in this section enable the Owner to monitor and analyze web traffic and can be used to keep track of User behavior.</p>
<h3 tabindex="0">Example LLC</h3>
<h3>Example LLC</h3>
<ul role="list">
<li tabindex="0">Personal Data: various types of Data as specified in the privacy policy of the service</li>
<li>Personal Data: various types of Data as specified in the privacy policy of the service</li>
<li><a href="https://example.com/privacy/" target="_blank" rel="noopener">Privacy Policy</a></li>
</ul>
</section>
<section aria-labelledby="external-content-heading">
<h2 id="external-content-heading" tabindex="0">External Content</h2>
<p tabindex="0">This type of service allows you to view content hosted on external platforms directly from the pages of this website and interact with them.</p>
<p tabindex="0">This type of service might still collect web traffic data for the pages where the service is installed, even when Users do not use it.</p>
<h2 id="external-content-heading">External Content</h2>
<p>This type of service allows you to view content hosted on external platforms directly from the pages of this website and interact with them.</p>
<p>This type of service might still collect web traffic data for the pages where the service is installed, even when Users do not use it.</p>
<h3 tabindex="0">Example LLC</h3>
<h3>Example LLC</h3>
<ul role="list">
<li tabindex="0">Personal Data: Usage Data; various types of Data as specified in the privacy policy of the service</li>
<li>Personal Data: Usage Data; various types of Data as specified in the privacy policy of the service</li>
<li><a href="https://example.com/privacy/" target="_blank" rel="noopener">Privacy Policy</a></li>
</ul>
</section>
<section aria-labelledby="hosting-heading">
<h2 id="hosting-heading" tabindex="0">Hosting and Infrastructure</h2>
<p tabindex="0">This type of service has the purpose of hosting Data and files that enable this website to exist.</p>
<p tabindex="0">Some services among those listed below, if any, may work through geographically distributed servers, making it difficult to determine the actual location where the Personal Data are stored.</p>
<h2 id="hosting-heading">Hosting and Infrastructure</h2>
<p>This type of service has the purpose of hosting Data and files that enable this website to exist.</p>
<p>Some services among those listed below, if any, may work through geographically distributed servers, making it difficult to determine the actual location where the Personal Data are stored.</p>
<h3 tabindex="0">Example LLC</h3>
<h3>Example LLC</h3>
<ul role="list">
<li tabindex="0">Personal Data: various types of Data as specified in the privacy policy of the service</li>
<li>Personal Data: various types of Data as specified in the privacy policy of the service</li>
<li><a href="https://example.com/privacy" target="_blank" rel="noopener">Privacy Policy</a></li>
</ul>
</section>