Release v4

This commit is contained in:
idotj 2024-02-07 16:45:38 +01:00
parent 182d3aeb9b
commit 3dc175a64e
20 changed files with 3798 additions and 1171 deletions

View File

@ -1,3 +1,4 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
@ -9,6 +10,7 @@ indent_size = 2
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
[**.min.js]

141
.gitignore vendored
View File

@ -1,4 +1,143 @@
# Created by https://www.toptal.com/developers/gitignore/api/linux
# Created by https://www.toptal.com/developers/gitignore/api/
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider

View File

@ -1,3 +1,16 @@
v4.0.0 - xx/02/2024
- Revamped JS (ES6)
- Renamed CSS classes
- Improved performance
- Removed the use of the word "toot" in code and comments
- Dynamic text for content-sensitive "Show/Hide" buttons
- Easier way to customize timeline settings
- Button to refresh timeline
- Improved theme color management
- Improve error messages
- Possibility to have several timelines on the same page
- Fixed inconsistency in video aspect ratio
v3.13.3 - 16/01/2024
- Add Docker compose
@ -49,7 +62,7 @@ v3.9.0 - 02/09/2023
v3.8.2 - 26/08/2023
- Add support to customized emojis
- Javascript refactoring to allow multiple requests
- JavaScript refactoring to allow multiple requests
v3.8.1 - 14/08/2023
- Show preview card from link, photo or video URL

264
README.md
View File

@ -1,4 +1,4 @@
# 🐘 Mastodon embed timeline
# 🐘 Mastodon embed timeline (new v4)
![Mastodon timeline widget screenshot](screenshot-light-dark.jpg "Mastodon timeline widget screenshot")
@ -7,108 +7,233 @@ Embed a mastodon feed timeline in your page, only with a CSS and JS file.
Demo running:
<https://codepen.io/ipuntoj/pen/MWppNGL>
## 🗂️ User guide
## 📋 Table of contents
### Install
- [**Installation**](#installation)
- [**Usage**](#usage)
- [Initialize](#initialize)
- [Local timeline](#local-timeline)
- [Profile timeline](#profile-timeline)
- [Hashtag timeline](#hashtag-timeline)
- [Customize](#customize)
- [API](#api)
- [Examples](#examples)
- [Browser support](#browser-support)
Just copy both files (_mastodon-timeline.css_ and _mastodon-timeline.js_ from /src folder) in your project folder.
## Installation
Now call each one in your page using the `<link>` and `<script>` tag:
Ready-to-use compiled and minified files to easily start.
- Download into your project the following files:
- `dist/mastodon-timeline.min.css`
- `dist/mastodon-timeline.min.js`
Now call the CSS and JS files in your HTML page using the `<link>` and `<script>` tags as follows in this example:
```html
<link rel="stylesheet" href="mastodon-timeline.css" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mastodon embed timeline</title>
<!-- CSS -->
<link href="path/to/mastodon-timeline.min.css" rel="stylesheet" />
</head>
<body>
<!-- Your HTML content -->
<!-- JavaScript -->
<script src="path/to/mastodon-timeline.min.js"></script>
</body>
</html>
```
```html
<script src="mastodon-timeline.js"></script>
```
## Usage
Then copy the following html structure:
### Initialize
The first step to get your timeline up is to add the following HTML structure in your page:
```html
<div class="mt-container">
<div id="mt-body" class="mt-body" role="feed">
<div class="loading-spinner"></div>
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
```
Great, you have a Mastodon timeline running in your page.
Then after that you can initialize the script by running:
### Setup
Edit the the JS file _mastodon-timeline.js_ and at the beginning find these two lines:
```javascript
instance_url: 'Your Mastodon instance (not including the last `/` symbol)',
timeline_type: 'local',
```js
const myTimeline = new MastodonTimeline();
```
Enter your Mastodon instance URL and reload the page. You should see toots from your local instance in your timeline.
By default it will show a timeline with 20 posts from the instance [mastodon.social](https://mastodon.social/public/local)
If you want to show a profile timeline then change the `timeline_type` to `profile` and set the following values:
If you are trying to use your timeline in a CMS such as Drupal, Wordpress, Joomla, etc... you will need to initialize your script when the whole page has loaded. In that case initialize the script by running:
```javascript
user_id: 'Your user ID number on Mastodon instance',
profile_name: 'Your user name on Mastodon instance (including the `@` symbol at the beginning)',
```js
window.addEventListener("load", () => {
const myTimeline = new MastodonTimeline();
});
```
If you prefer to show a timeline with a specific hashtag then change the `timeline_type` to `hashtag` and enter the name of the hashtag:
#### Local timeline
```javascript
hashtag_name: 'YourHashtag (not including the `#` symbol)',
Add the following option/value when initializing the timeline:
```js
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
});
```
Also you have some parameters to customize your embed timeline:
It will show a timeline with posts from the instance [mastodon.online](https://mastodon.online/public/local)
```javascript
// Preferred color theme: 'light', 'dark' or 'auto'. Default: auto
default_theme: "auto",
#### Profile timeline
// Maximum amount of toots to get. Default: 20
toots_limit: "20",
Add the following options/values when initializing the timeline:
// Hide unlisted toots. Default: don't hide
hide_unlisted: false,
// Hide boosted toots. Default: don't hide
hide_reblog: false,
// Hide replies toots. Default: don't hide
hide_replies: false,
// Hide video image preview and load video player instead. Default: don't hide
hide_video_preview: false,
// Hide preview for links. Default: don't hide
hide_preview_link: false,
// Hide custom emojis available on the server. Default: don't hide
hide_emojos: false,
// Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag. Default: don't apply
markdown_blockquote: false,
// Limit the text content to a maximum number of lines. Default: 0 (unlimited)
text_max_lines: "0",
// Hide replies, boosts and favourites toots counter. Default: don't hide
hide_counter_bar: false,
// Customize the text of the link pointing to the Mastodon page (appears after the last toot)
link_see_more: "See more posts at Mastodon",
```js
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "profile",
userId: "000180745",
profileName: "@idotj",
});
```
### Tip
It will show a timeline with posts from my Mastodon profile [@idotj](https://mastodon.online/@idotj)
To setup a **profile timeline** you will need your `user_id` number. If you don't know it you have two ways to get it:
If you don't know your `userId` you have two ways to get it:
- Copy the url below and paste it in a new tab. Remember to replace the words `INSTANCE` and `USERNAME` with your current values in the url:
<https://INSTANCE/api/v1/accounts/lookup?acct=USERNAME>
The first value you see in the list is your `id` number.
- Click on the link below and put your `@USERNAME` and `@INSTANCE` in the input field:
<a href="https://prouser123.me/mastodon-userid-lookup/" target="_blank" rel="noopener">https://prouser123.me/mastodon-userid-lookup/</a>
[https://prouser123.me/mastodon-userid-lookup/](https://prouser123.me/mastodon-userid-lookup/)
#### Hashtag timeline
Add the following options/values when initializing the timeline:
```js
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "hashtag",
hashtagName: "fediverse",
});
```
It will show a timeline with posts containing the hashtag [#fediverse](https://mastodon.online/tags/fediverse)
### Customize
You can pass more options/values to personalize your timeline:
```js
// Id of the <div> containing the timeline
mtContainerId: "mt-container",
// Mastodon instance Url (including https://)
instanceUrl: "https://mastodon.social",
// Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag'. Default: local
timelineType: "local",
// Your user ID number on Mastodon instance. Leave it empty if you didn't choose 'profile' as type of timeline
userId: "",
// Your user name on Mastodon instance (including the @ symbol at the beginning). Leave it empty if you didn't choose 'profile' as type of timeline
profileName: "",
// The name of the hashtag (not including the # symbol). Leave it empty if you didn't choose 'hashtag' as type of timeline
hashtagName: "",
// Class name for the loading spinner (also used in CSS file)
spinnerClass: "mt-loading-spinner",
// Preferred color theme: 'light', 'dark' or 'auto'. Default: auto
defaultTheme: "auto",
// Maximum number of posts to request to the server. Default: 20
maxNbPostFetch: "20",
// Maximum number of posts to show in the timeline. Default: 20
maxNbPostShow: "20",
// Hide unlisted posts. Default: don't hide
hideUnlisted: false,
// Hide boosted posts. Default: don't hide
hideReblog: false,
// Hide replies posts. Default: don't hide
hideReplies: false,
// Hide video image preview and load video player instead. Default: don't hide
hideVideoPreview: false,
// Hide preview card if post contains a link, photo or video from a Url. Default: don't hide
hidePreviewLink: false,
// Hide custom emojis available on the server. Default: don't hide
hideEmojos: false,
// Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag. Default: don't apply
markdownBlockquote: false,
// Hide replies, boosts and favourites posts counter. Default: don't hide
hideCounterBar: false,
// Limit the text content to a maximum number of lines. Default: 0 (unlimited)
txtMaxLines: "0",
// Customize the text of the button used for showing/hiding sensitive/spolier text
btnShowMore: "SHOW MORE",
btnShowLess: "SHOW LESS",
// Customize the text of the button used for showing sensitive/spolier media content
btnShowContent: "SHOW CONTENT",
// Customize the text of the button pointing to the Mastodon page placed at the end of the timeline. Leave the value empty to hide it
btnSeeMore: "See more posts at Mastodon",
// Customize the text of the button reloading the list of posts placed at the end of the timeline. Leave the value empty to hide it
btnReload: "Refresh",
```
### API
| Function | Description |
| --- | --- |
| `mtColorTheme(themeType)` | Apply a theme color. `themeType` accepts only two values: `light` or `dark` |
| `mtUpdate()` | Reload the timeline by fetching the lastest posts |
### Examples
The folder `/examples` contains several demos in HTML to play with. Just download the folder and open each HTML file in your favorite browser.
Also, you have a Docker file to perform your tests if needed. Simply inside the `/examples` folder run:
```terminal
docker compose up
```
### Browser support
Mastodon embed timeline is supported on the latest versions of the following browsers:
- Chrome
- Firefox
- Edge
- Safari
- Brave
- Opera
## 🚀 Improve me
@ -120,5 +245,8 @@ GNU Affero General Public License v3.0
## 💬 FAQ
Check the [closed issues](https://gitlab.com/idotj/mastodon-embed-feed-timeline/-/issues/?sort=created_date&state=closed&first_page_size=20), you might find your question there.
Check the [closed issues](https://gitlab.com/idotj/mastodon-embed-feed-timeline/-/issues/?sort=created_date&state=closed&first_page_size=20), you might find your question there.
If nothing matches with your problem, check the [open issues](https://gitlab.com/idotj/mastodon-embed-feed-timeline/-/issues/?sort=created_date&state=opened&first_page_size=20) or feel free to create a new one.
Looking for a previous version of Mastodon embed timeline? Check on the tags list to see all the released versions: [Tags version history](https://gitlab.com/idotj/mastodon-embed-feed-timeline/-/tags)

1
dist/mastodon-timeline.min.css vendored Normal file

File diff suppressed because one or more lines are too long

7
dist/mastodon-timeline.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,462 @@
/* Mastodon embed feed timeline v4.0.0 */
/* More info at: */
/* https://gitlab.com/idotj/mastodon-embed-feed-timeline */
/* Variables */
.mt-container,
.mt-container[data-theme="light"] {
--mt-txt-max-lines: none;
--mt-color-bg: #fff;
--mt-color-bg-hover: #d9e1e8;
--mt-color-line-gray: #c0cdd9;
--mt-color-contrast-gray: #606984;
--mt-color-content-txt: #000;
--mt-color-link: #3a3bff;
--mt-color-error-txt: #8b0000;
--mt-color-btn-bg: #6364ff;
--mt-color-btn-bg-hover: #563acc;
--mt-color-btn-txt: #fff;
}
.mt-container[data-theme="dark"] {
--mt-color-bg: #282c37;
--mt-color-bg-hover: #313543;
--mt-color-line-gray: #393f4f;
--mt-color-contrast-gray: #606984;
--mt-color-content-txt: #fff;
--mt-color-link: #8c8dff;
--mt-color-error-txt: #fe6c6c;
}
/* Reset CSS */
.mt-container button {
font: inherit;
}
.mt-container a,
.mt-container button {
cursor: pointer;
}
/* Main container */
.mt-container {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
position: relative;
background-color: var(--mt-color-bg);
scrollbar-color: var(--mt-color-contrast-gray) var(--mt-color-bg);
scrollbar-width: auto;
}
.mt-container::-webkit-scrollbar {
width: 0.25rem;
height: 0.25rem;
}
.mt-container::-webkit-scrollbar-thumb {
background-color: var(--mt-color-contrast-gray);
border: none;
border-radius: 3rem;
}
.mt-container::-webkit-scrollbar-thumb:hover,
.mt-container::-webkit-scrollbar-thumb:active {
background-color: var(--mt-color-contrast-gray);
}
.mt-container::-webkit-scrollbar-track {
background-color: var(--mt-color-bg);
border: none;
border-radius: 0;
}
.mt-container::-webkit-scrollbar-track:hover,
.mt-container::-webkit-scrollbar-track:active,
.mt-container::-webkit-scrollbar-corner {
background-color: var(--mt-color-bg);
}
.mt-container a:link,
.mt-container a:active,
.mt-container a {
text-decoration: none;
color: var(--mt-color-link);
}
.mt-container a:not(.mt-post-preview):hover {
text-decoration: underline;
}
.mt-body {
padding: 1rem clamp(0.25rem, 4vw, 1rem);
white-space: pre-wrap;
word-wrap: break-word;
margin-bottom: 1rem;
}
.mt-body .invisible {
font-size: 0;
line-height: 0;
display: inline-block;
width: 0;
height: 0;
position: absolute;
}
/* Post container */
.mt-post {
margin: 0.25rem;
padding: 1rem 0.5rem;
position: relative;
min-height: 3.75rem;
background-color: transparent;
border-bottom: 1px solid var(--mt-color-line-gray);
}
.mt-post:hover,
.mt-post:focus {
cursor: pointer;
background-color: var(--mt-color-bg-hover);
}
.mt-post p:last-child {
margin-bottom: 0;
}
/* User avatar */
.mt-post-avatar {
margin-right: 0.75rem;
}
.mt-post-avatar-standard {
width: 2.25rem;
height: 2.25rem;
}
.mt-post-avatar-boosted {
width: 3rem;
height: 3rem;
position: relative;
}
.mt-post-avatar-image-big img {
aspect-ratio: 1/1;
width: 2.25rem;
height: 2.25rem;
border-radius: 0.25rem;
overflow: hidden;
}
.mt-post-avatar-image-small img {
aspect-ratio: 1/1;
width: 1.5rem;
height: 1.5rem;
top: 1.5rem;
left: 1.5rem;
position: absolute;
border-radius: 0.25rem;
overflow: hidden;
}
/* User name and date */
.mt-post-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
}
.mt-post-header-user {
font-weight: 600;
margin-top: 0.5rem;
padding-right: 1rem;
}
.mt-post-header-user > a {
display: flex;
align-items: flex-start;
color: var(--mt-color-content-txt) !important;
overflow-wrap: anywhere;
}
.mt-post-header-date {
font-size: 0.75rem;
text-align: right;
margin: 0.5rem 0 0 auto;
}
.mt-post-header-date > a {
color: var(--mt-color-contrast-gray) !important;
}
/* Text */
.mt-post-txt {
margin-bottom: 1rem;
color: var(--mt-color-content-txt);
}
.mt-post-txt .spoiler-txt-hidden {
display: none;
}
.mt-post-txt.truncate {
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: var(--mt-txt-max-lines);
-webkit-box-orient: vertical;
}
.mt-post-txt:not(.truncate) .ellipsis::after {
content: "...";
}
.mt-post-txt blockquote {
border-left: 0.25rem solid var(--mt-color-line-gray);
margin-left: 0;
padding-left: 0.5rem;
}
.mt-post-header-user .mt-custom-emoji,
.mt-post-txt .mt-custom-emoji {
height: 1.5rem;
min-width: 1.5rem;
margin-bottom: -0.25rem;
width: auto;
}
/* Poll */
.mt-post-poll {
margin-bottom: 1rem;
color: var(--mt-color-content-txt);
}
.mt-post-poll ul {
list-style: none;
padding: 0;
margin: 0;
}
.mt-post-poll ul li {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.mt-post-poll.mt-post-poll-expired ul li {
color: var(--mt-color-contrast-gray);
}
.mt-post-poll ul li:not(:last-child) {
margin-bottom: 0.25rem;
}
.mt-post-poll ul li:before {
content: "◯";
padding-right: 0.5rem;
}
.mt-post-poll.mt-post-poll-expired ul li:before {
content: "";
padding-right: 0;
}
/* Medias */
.mt-post-media {
position: relative;
overflow: hidden;
margin-bottom: 1rem;
}
.mt-post-media-spoiler > img,
.mt-post-media-spoiler > audio,
.mt-post-media-spoiler > video,
.mt-post-media-spoiler > .mt-post-media-play-icon {
filter: blur(2rem);
pointer-events: none;
}
.mt-post-media > audio {
width: 100%;
position: relative;
z-index: 1;
}
.mt-post-media > img,
.mt-post-media > video {
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: var(--mt-color-content-txt);
}
.mt-post-media.mt-loading-spinner .mt-post-media-play-icon {
display: none;
}
.mt-post-media-play-icon {
display: flex;
position: absolute;
width: 3rem;
height: 3rem;
top: calc(50% - 1.5rem);
left: calc(50% - 1.5rem);
justify-content: center;
align-items: center;
background-color: transparent;
border: none;
cursor: pointer;
}
.mt-post-media-play-icon > svg {
width: 2.5rem;
height: 2.5rem;
fill: var(--mt-color-bg);
stroke: var(--mt-color-content-txt);
stroke-width: 1px;
}
/* Preview link */
.mt-post-preview {
min-height: 4rem;
display: flex;
flex-direction: row;
border: 1px solid var(--mt-color-line-gray);
border-radius: 0.5rem;
color: var(--mt-color-link);
font-size: 0.8rem;
margin: 1rem 0;
overflow: hidden;
}
.mt-post-preview-image {
width: 40%;
align-self: stretch;
}
.mt-post-preview-image img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
color: var(--mt-color-content-txt);
}
.mt-post-preview-noImage {
width: 40%;
font-size: 1.5rem;
align-self: center;
text-align: center;
}
.mt-post-preview-content {
width: 60%;
display: flex;
align-self: center;
flex-direction: column;
padding: 0.5rem 1rem;
gap: 0.5rem;
}
.mt-post-preview-title {
font-weight: 600;
}
/* Counter bar */
.mt-post-counter-bar {
display: flex;
min-width: 6rem;
max-width: 40rem;
justify-content: space-between;
color: var(--mt-color-contrast-gray);
}
.mt-post-counter-bar-replies,
.mt-post-counter-bar-reblog,
.mt-post-counter-bar-favorites {
display: flex;
font-size: 0.75rem;
gap: 0.25rem;
align-items: center;
opacity: 0.5;
}
.mt-post-counter-bar-replies > svg,
.mt-post-counter-bar-reblog > svg,
.mt-post-counter-bar-favorites > svg {
width: 1rem;
fill: var(--mt-color-contrast-gray);
}
/* Buttons */
.mt-container .mt-btn-dark {
display: flex;
border-radius: 0.25rem;
background-color: var(--mt-color-line-gray);
border: 0;
color: var(--mt-color-content-txt);
font-weight: 600;
font-size: 0.75rem;
text-align: center;
padding: 0 0.5rem;
line-height: 1.25rem;
vertical-align: top;
}
.mt-container .mt-btn-violet,
.mt-container a.mt-btn-violet {
display: flex;
gap: 0.5rem;
border-radius: 0.25rem;
border: 0.5rem;
padding: 0.5rem 0.75rem;
font-size: 1rem;
font-weight: 600;
text-align: center;
background-color: var(--mt-color-btn-bg);
color: var(--mt-color-btn-txt);
}
.mt-container .mt-btn-violet:hover,
.mt-container a.mt-btn-violet:hover {
background-color: var(--mt-color-btn-bg-hover);
text-decoration: none;
}
.mt-post-txt .mt-btn-spoiler {
display: inline-block;
}
.mt-post-media.mt-loading-spinner > .mt-btn-spoiler {
display: none;
}
.mt-post-media > .mt-btn-spoiler {
position: absolute;
top: 50%;
left: 50%;
z-index: 2;
transform: translate(-50%, -50%);
}
/* Error */
.mt-error {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
height: calc(100% - 3.5rem);
width: calc(100% - 4.5rem);
justify-content: center;
align-items: center;
color: var(--mt-color-error-txt);
padding: 0.75rem;
text-align: center;
}
.mt-error-icon {
font-size: 2rem;
}
.mt-error-message {
width: 100%;
padding: 1rem 0;
}
.mt-error-message hr {
color: var(--mt-color-line-gray);
}
/* Loading spinner */
.mt-body > .mt-loading-spinner {
position: absolute;
width: 3rem;
height: 3rem;
margin: auto;
top: calc(50% - 1.5rem);
right: calc(50% - 1.5rem);
}
.mt-loading-spinner {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'%3E%3Cg%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'/%3E%3Cpath d='M64 6.69a57.3 57.3 0 1 1 0 114.61A57.3 57.3 0 0 1 6.69 64' fill='none' stroke='%23404040' stroke-width='12'/%3E%3C/g%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center center;
background-color: transparent;
background-size: min(2.5rem, calc(100% - 0.5rem));
}
/* Footer */
.mt-footer {
display: flex;
flex-flow: wrap;
margin: auto auto 2rem auto;
padding: 0 1.5rem;
gap: 1.5rem;
align-items: center;
justify-content: center;
}
/* Hidden elements */
.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}

View File

@ -3,7 +3,7 @@
#
# $ docker compose up
version: '3.3'
version: "3.3"
services:
lighttpd:
@ -12,9 +12,13 @@ services:
- "8080:80"
- "8443:443"
volumes:
- ./src/index.html:/var/www/index.html
- ./src/mastodon-timeline.js:/var/www/mastodon-timeline.js
- ./src/mastodon-timeline.css:/var/www/mastodon-timeline.css
- ./css/mastodon-timeline.css:/var/www/css/mastodon-timeline.css
- ./js/mastodon-timeline.js:/var/www/js/mastodon-timeline.js
- ./local-timeline.html:/var/www/index.html
- ./profile-timeline.html:/var/www/profile-timeline.html
- ./hashtag-timeline.html:/var/www/hashtag-timeline.html
- ./theme-timeline.html:/var/www/theme-timeline.html
- ./multiple-timelines.html:/var/www/multiple-timelines.html
environment:
- PORT=80
- SERVER_NAME=mastodon-timeline

View File

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./css/mastodon-timeline.css" />
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
height: 100%;
background: lightgrey;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-main-container {
display: flex;
flex-direction: row;
gap: 2rem;
height: 100%;
justify-content: center;
align-items: center;
padding: 1rem;
}
.dummy-wrapper-text,
.dummy-wrapper-timeline {
width: 50%;
max-width: 30rem;
height: calc(100% - 4rem);
padding: 0 1rem;
}
.dummy-wrapper-text h1,
.dummy-wrapper-text h2,
.dummy-wrapper-text p {
margin: 0 0 1rem 0;
}
.dummy-wrapper-text pre {
display: flex;
background: lightsteelblue;
border-left: 3px solid #563acc;
color: midnightblue;
page-break-inside: avoid;
font-family: monospace;
line-height: 1.5;
max-width: 100%;
overflow: auto;
word-wrap: break-word;
}
.dummy-wrapper-text hr {
margin: 2rem 0;
}
</style>
</head>
<body>
<div class="dummy-main-container">
<!-- Title + Explained details of the example -->
<div class="dummy-wrapper-text">
<h1>🐘 Mastodon embed timeline</h1>
<h2>Profile timeline</h2>
<p>
This example shows posts containing the hashtag
<br />
<a
href="https://mastodon.online/tags/fediverse"
target="_blank"
rel="nofollow noopener noreferrer"
>#fediverse</a
>
</p>
<p>It has been initialized with the following script:</p>
<pre>
<code>
&lt;script&gt;
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "hashtag",
hashtagName: "fediverse",
});
&lt;/script&gt;
</code>
</pre>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline -->
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="./js/mastodon-timeline.js"></script>
<script>
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "hashtag",
hashtagName: "fediverse",
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./css/mastodon-timeline.css" />
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
height: 100%;
background: lightgrey;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-main-container {
display: flex;
flex-direction: row;
gap: 2rem;
height: 100%;
justify-content: center;
align-items: center;
padding: 1rem;
}
.dummy-wrapper-text,
.dummy-wrapper-timeline {
width: 50%;
max-width: 30rem;
height: calc(100% - 4rem);
padding: 0 1rem;
}
.dummy-wrapper-text h1,
.dummy-wrapper-text h2,
.dummy-wrapper-text p {
margin: 0 0 1rem 0;
}
.dummy-wrapper-text pre {
display: flex;
background: lightsteelblue;
border-left: 3px solid #563acc;
color: midnightblue;
page-break-inside: avoid;
font-family: monospace;
line-height: 1.5;
max-width: 100%;
overflow: auto;
word-wrap: break-word;
}
.dummy-wrapper-text hr {
margin: 2rem 0;
}
</style>
</head>
<body>
<div class="dummy-main-container">
<!-- Title + Explained details of the example -->
<div class="dummy-wrapper-text">
<h1>🐘 Mastodon embed timeline</h1>
<h2>Local timeline</h2>
<p>
This example shows 20 posts from the following instance:
<br />
<a
href="https://mastodon.social/public/local"
target="_blank"
rel="nofollow noopener noreferrer"
>mastodon.social</a
>
</p>
<p>
It has the default parameters and has been initialized with the
following script:
</p>
<pre>
<code>
&lt;script&gt;
const myTimeline = new MastodonTimeline();
&lt;/script&gt;
</code>
</pre>
<hr />
<p>
To change the current instance and show a different local timeline,
for example:
<br />
<a
href="https://mastodon.online/public/local"
target="_blank"
rel="nofollow noopener noreferrer"
>mastodon.online</a
>
</p>
<p>
You just need to initialize the script by passing the custom option
<strong>instanceUrl</strong> with the value
<strong>"https://mastodon.online"</strong> as follows in the example:
</p>
<pre>
<code>
&lt;script&gt;
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online"
});
&lt;/script&gt;
</code>
</pre>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline -->
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="./js/mastodon-timeline.js"></script>
<script>
const myTimeline = new MastodonTimeline();
</script>
</body>
</html>

View File

@ -0,0 +1,165 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./css/mastodon-timeline.css" />
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
height: 100%;
background: lightgrey;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-main-container {
display: flex;
flex-direction: row;
gap: 2rem;
height: 100%;
justify-content: center;
align-items: center;
padding: 1rem;
}
.dummy-wrapper-text,
.dummy-wrapper-timeline {
width: 50%;
max-width: 30rem;
height: calc(100% - 4rem);
padding: 0 1rem;
}
.dummy-wrapper-text h1,
.dummy-wrapper-text h2,
.dummy-wrapper-text p {
margin: 0 0 1rem 0;
}
.dummy-wrapper-text pre {
display: flex;
background: lightsteelblue;
border-left: 3px solid #563acc;
color: midnightblue;
page-break-inside: avoid;
font-family: monospace;
line-height: 1.5;
max-width: 100%;
overflow: auto;
word-wrap: break-word;
}
.dummy-wrapper-text hr {
margin: 2rem 0;
}
</style>
</head>
<body>
<div class="dummy-main-container">
<!-- Title + Explained details of the example -->
<div class="dummy-wrapper-text">
<h1>🐘 Mastodon embed timeline</h1>
<h2>Multiple timeline</h2>
<p>
This example shows 3 local timelines running in the same page:
<br />
<a
href="https://mastodon.social/public/local"
target="_blank"
rel="nofollow noopener noreferrer"
>mastodon.social</a
>
<br />
<a
href="https://mastodon.online/public/local"
target="_blank"
rel="nofollow noopener noreferrer"
>mastodon.online</a
>
<br />
<a
href="https://mstdn.social/public/local"
target="_blank"
rel="nofollow noopener noreferrer"
>mstdn.social</a
>
</p>
<p>It has been initialized with the following script:</p>
<pre>
<code>
&lt;script&gt;
const myTimeline1 = new MastodonTimeline({
mtContainerId: "mt-container-01",
instanceUrl: "https://mastodon.social",
});
const myTimeline2 = new MastodonTimeline({
mtContainerId: "mt-container-02",
instanceUrl: "https://mastodon.online",
});
const myTimeline3 = new MastodonTimeline({
mtContainerId: "mt-container-03",
instanceUrl: "https://mstdn.social",
});
&lt;/script&gt;
</code>
</pre>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline 01 -->
<div id="mt-container-01" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline 02 -->
<div id="mt-container-02" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline 02 -->
<div id="mt-container-03" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="./js/mastodon-timeline.js"></script>
<script>
const myTimeline1 = new MastodonTimeline({
mtContainerId: "mt-container-01",
instanceUrl: "https://mastodon.social",
});
const myTimeline2 = new MastodonTimeline({
mtContainerId: "mt-container-02",
instanceUrl: "https://mastodon.online",
});
const myTimeline3 = new MastodonTimeline({
mtContainerId: "mt-container-03",
instanceUrl: "https://mstdn.social",
});
</script>
</body>
</html>

View File

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./css/mastodon-timeline.css" />
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
height: 100%;
background: lightgrey;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-main-container {
display: flex;
flex-direction: row;
gap: 2rem;
height: 100%;
justify-content: center;
align-items: center;
padding: 1rem;
}
.dummy-wrapper-text,
.dummy-wrapper-timeline {
width: 50%;
max-width: 30rem;
height: calc(100% - 4rem);
padding: 0 1rem;
}
.dummy-wrapper-text h1,
.dummy-wrapper-text h2,
.dummy-wrapper-text p {
margin: 0 0 1rem 0;
}
.dummy-wrapper-text pre {
display: flex;
background: lightsteelblue;
border-left: 3px solid #563acc;
color: midnightblue;
page-break-inside: avoid;
font-family: monospace;
line-height: 1.5;
max-width: 100%;
overflow: auto;
word-wrap: break-word;
}
.dummy-wrapper-text hr {
margin: 2rem 0;
}
</style>
</head>
<body>
<div class="dummy-main-container">
<!-- Title + Explained details of the example -->
<div class="dummy-wrapper-text">
<h1>🐘 Mastodon embed timeline</h1>
<h2>Profile timeline</h2>
<p>
This example shows posts from my Mastodon profile
<br />
<a
href="https://mastodon.online/@idotj"
target="_blank"
rel="nofollow noopener noreferrer"
>@idotj</a
>
</p>
<p>It has been initialized with the following script:</p>
<pre>
<code>
&lt;script&gt;
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "profile",
userId: "000180745",
profileName: "@idotj",
});
&lt;/script&gt;
</code>
</pre>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline -->
<div id="mt-container" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="./js/mastodon-timeline.js"></script>
<script>
const myTimeline = new MastodonTimeline({
instanceUrl: "https://mastodon.online",
timelineType: "profile",
userId: "000180745",
profileName: "@idotj",
});
</script>
</body>
</html>

View File

@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./css/mastodon-timeline.css" />
<style>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
height: 100%;
background: lightgrey;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-main-container {
display: flex;
flex-direction: row;
gap: 2rem;
height: 100%;
justify-content: center;
align-items: center;
padding: 1rem;
}
.dummy-wrapper-text,
.dummy-wrapper-timeline {
width: 50%;
max-width: 30rem;
height: calc(100% - 4rem);
padding: 0 1rem;
}
.dummy-wrapper-text h1,
.dummy-wrapper-text h2,
.dummy-wrapper-text p {
margin: 0 0 1rem 0;
}
.dummy-wrapper-text pre {
display: flex;
background: lightsteelblue;
border-left: 3px solid #563acc;
color: midnightblue;
page-break-inside: avoid;
font-family: monospace;
line-height: 1.5;
max-width: 100%;
overflow: auto;
word-wrap: break-word;
}
.dummy-wrapper-text hr {
margin: 2rem 0;
}
.dummy-buttons-container {
display: flex;
flex-direction: row;
gap: 1rem;
margin-bottom: 1rem;
}
.dummy-buttons-container button {
display: flex;
border-radius: 0.25rem;
border: 0.5rem;
padding: 0.5rem 0.75rem;
font-family: inherit;
font-size: 1rem;
font-weight: 600;
text-align: center;
background-color: #6364ff;
color: #fff;
cursor: pointer;
}
.dummy-buttons-container button:hover {
background-color: #563acc;
}
</style>
</head>
<body>
<div class="dummy-main-container">
<!-- Title + Explained details of the example -->
<div class="dummy-wrapper-text">
<h1>🐘 Mastodon embed timeline</h1>
<h2>Light timeline</h2>
<p>This example shows a timeline in light color:</p>
<p>It has been initialized with the following script:</p>
<pre>
<code>
&lt;script&gt;
const myTimeline1 = new MastodonTimeline({
mtContainerId: "mt-container-01",
defaultTheme: "light",
});
&lt;/script&gt;
</code>
</pre>
<hr />
<h2>Dark timeline</h2>
<p>This example shows a timeline in dark color:</p>
<p>It has been initialized with the following script:</p>
<pre>
<code>
&lt;script&gt;
const myTimeline2 = new MastodonTimeline({
mtContainerId: "mt-container-02",
defaultTheme: "dark",
});
&lt;/script&gt;
</code>
</pre>
<hr />
<h2>Theme API</h2>
<p>
You can change your timeline color calling the function <strong>mtColorTheme()</strong>
</p>
<div class="dummy-buttons-container">
<button onclick="myTimeline1.mtColorTheme('dark')">
Switch 1st timeline to dark theme
</button>
<button onclick="myTimeline2.mtColorTheme('light')">
Switch 2nd timeline to light theme
</button>
</div>
<pre>
<code>
&lt;button onclick="myTimeline1.mtColorTheme('dark')"&gt;
Switch 1st timeline to dark theme
&lt;/button&gt;
&lt;button onclick="myTimeline2.mtColorTheme('light')"&gt;
Switch 2nd timeline to light theme
&lt;/button&gt;
</code>
</pre>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline 01 -->
<div id="mt-container-01" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
<div class="dummy-wrapper-timeline">
<!-- Mastodon Timeline 02 -->
<div id="mt-container-02" class="mt-container">
<div class="mt-body" role="feed">
<div class="mt-loading-spinner"></div>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="./js/mastodon-timeline.js"></script>
<script>
const myTimeline1 = new MastodonTimeline({
mtContainerId: "mt-container-01",
defaultTheme: "light",
});
const myTimeline2 = new MastodonTimeline({
mtContainerId: "mt-container-02",
defaultTheme: "dark",
});
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -1,45 +0,0 @@
<!DOCTYPE html>
<html lang="en" data-theme="">
<head>
<meta charset="utf-8" />
<title>Mastodon embed timeline</title>
<meta name="author" content="i.j" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="keywords" content="mastodon, embed timeline" />
<meta name="description" content="Mastodon embed timeline" />
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./mastodon-timeline.css" />
<style>
html {
height: 100%;
}
body {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
background: lightslategray;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0;
}
.dummy-wrapper {
width: calc(100% - 1rem);
max-width: 26rem;
height: calc(100% - 3rem);
}
</style>
</head>
<body>
<div class="dummy-wrapper">
<div class="mt-container">
<div id="mt-body" class="mt-body" role="feed">
<div class="loading-spinner"></div>
</div>
</div>
</div>
<script src="./mastodon-timeline.js"></script>
</body>
</html>

View File

@ -1,78 +1,89 @@
/* Mastodon embed feed timeline v3.13.2 */
/* Mastodon embed feed timeline v4.0.0 */
/* More info at: */
/* https://gitlab.com/idotj/mastodon-embed-feed-timeline */
/* Variables */
:root {
--text-max-lines: none;
.mt-container,
.mt-container[data-theme="light"] {
--mt-txt-max-lines: none;
--mt-color-bg: #fff;
--mt-color-bg-hover: #d9e1e8;
--mt-color-line-gray: #c0cdd9;
--mt-color-contrast-gray: #606984;
--mt-color-content-txt: #000;
--mt-color-link: #3a3bff;
--mt-color-error-txt: #8b0000;
--mt-color-btn-bg: #6364ff;
--mt-color-btn-bg-hover: #563acc;
--mt-color-btn-txt: #fff;
}
.mt-container[data-theme="dark"] {
--mt-color-bg: #282c37;
--mt-color-bg-hover: #313543;
--mt-color-line-gray: #393f4f;
--mt-color-contrast-gray: #606984;
--mt-color-content-txt: #fff;
--mt-color-link: #8c8dff;
--mt-color-error-txt: #fe6c6c;
}
/* Theme colors */
:root,
html[data-theme="light"] {
--bg-color: #fff;
--bg-hover-color: #d9e1e8;
--line-gray-color: #c0cdd9;
--contrast-gray-color: #606984;
--content-text: #000;
--link-color: #3a3bff;
--error-text-color: #8b0000;
/* Reset CSS */
.mt-container button {
font: inherit;
}
html[data-theme="dark"] {
--bg-color: #282c37;
--bg-hover-color: #313543;
--line-gray-color: #393f4f;
--contrast-gray-color: #606984;
--content-text: #fff;
--link-color: #8c8dff;
--error-text-color: #fe6c6c;
.mt-container a,
.mt-container button {
cursor: pointer;
}
/* Main container */
.mt-container {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
position: relative;
background-color: var(--bg-color);
scrollbar-color: var(--line-gray-color) var(--bg-color);
scrollbar-width: thin;
background-color: var(--mt-color-bg);
scrollbar-color: var(--mt-color-contrast-gray) var(--mt-color-bg);
scrollbar-width: auto;
}
.mt-container::-webkit-scrollbar {
width: 0.25rem;
height: 0.25rem;
}
.mt-container::-webkit-scrollbar-thumb {
background-color: var(--line-gray-color);
background-color: var(--mt-color-contrast-gray);
border: none;
border-radius: 3rem;
}
.mt-container::-webkit-scrollbar-thumb:hover,
.mt-container::-webkit-scrollbar-thumb:active {
background-color: var(--line-gray-color);
background-color: var(--mt-color-contrast-gray);
}
.mt-container::-webkit-scrollbar-track {
background-color: var(--bg-color);
background-color: var(--mt-color-bg);
border: none;
border-radius: 0;
}
.mt-container::-webkit-scrollbar-track:hover,
.mt-container::-webkit-scrollbar-track:active,
.mt-container::-webkit-scrollbar-corner {
background-color: var(--bg-color);
background-color: var(--mt-color-bg);
}
.mt-container a:link,
.mt-container a:active,
.mt-container a {
text-decoration: none;
color: var(--link-color);
color: var(--mt-color-link);
}
.mt-container a:not(.mt-toot-preview):hover {
.mt-container a:not(.mt-post-preview):hover {
text-decoration: underline;
}
.mt-body {
padding: 1rem clamp(0.25rem, 4vw, 1.5rem);
padding: 1rem clamp(0.25rem, 4vw, 1rem);
white-space: pre-wrap;
word-wrap: break-word;
margin-bottom: 1rem;
}
.mt-body .invisible {
font-size: 0;
@ -83,45 +94,45 @@ html[data-theme="dark"] {
position: absolute;
}
/* Toot container */
.mt-toot {
/* Post container */
.mt-post {
margin: 0.25rem;
padding: 1rem 0.5rem;
position: relative;
min-height: 3.75rem;
background-color: transparent;
border-bottom: 1px solid var(--line-gray-color);
border-bottom: 1px solid var(--mt-color-line-gray);
}
.mt-toot:hover,
.mt-toot:focus {
.mt-post:hover,
.mt-post:focus {
cursor: pointer;
background-color: var(--bg-hover-color);
background-color: var(--mt-color-bg-hover);
}
.mt-toot p:last-child {
.mt-post p:last-child {
margin-bottom: 0;
}
/* User avatar */
.mt-toot-avatar {
.mt-post-avatar {
margin-right: 0.75rem;
}
.mt-toot-avatar-standard {
.mt-post-avatar-standard {
width: 2.25rem;
height: 2.25rem;
}
.mt-toot-avatar-boosted {
.mt-post-avatar-boosted {
width: 3rem;
height: 3rem;
position: relative;
}
.mt-toot-avatar-image-big img {
.mt-post-avatar-image-big img {
aspect-ratio: 1/1;
width: 2.25rem;
height: 2.25rem;
border-radius: 0.25rem;
overflow: hidden;
}
.mt-toot-avatar-image-small img {
.mt-post-avatar-image-small img {
aspect-ratio: 1/1;
width: 1.5rem;
height: 1.5rem;
@ -133,59 +144,56 @@ html[data-theme="dark"] {
}
/* User name and date */
.mt-toot-header {
.mt-post-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
}
.mt-toot-header-user {
.mt-post-header-user {
font-weight: 600;
margin-top: 0.5rem;
padding-right: 1rem;
}
.mt-toot-header-user > a {
.mt-post-header-user > a {
display: flex;
align-items: flex-start;
color: var(--content-text) !important;
color: var(--mt-color-content-txt) !important;
overflow-wrap: anywhere;
}
.mt-toot-header-date {
.mt-post-header-date {
font-size: 0.75rem;
text-align: right;
margin: 0.5rem 0 0 auto;
}
.mt-toot-header-date > a {
color: var(--contrast-gray-color) !important;
.mt-post-header-date > a {
color: var(--mt-color-contrast-gray) !important;
}
/* Text */
.mt-toot-text {
.mt-post-txt {
margin-bottom: 1rem;
color: var(--content-text);
color: var(--mt-color-content-txt);
}
.mt-toot-text .spoiler-btn {
display: inline-block;
}
.mt-toot-text .spoiler-text-hidden {
.mt-post-txt .spoiler-txt-hidden {
display: none;
}
.mt-toot-text.truncate {
.mt-post-txt.truncate {
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: var(--text-max-lines);
-webkit-line-clamp: var(--mt-txt-max-lines);
-webkit-box-orient: vertical;
}
.mt-toot-text:not(.truncate) .ellipsis::after {
.mt-post-txt:not(.truncate) .ellipsis::after {
content: "...";
}
.mt-toot-text blockquote {
border-left: 0.25rem solid var(--line-gray-color);
.mt-post-txt blockquote {
border-left: 0.25rem solid var(--mt-color-line-gray);
margin-left: 0;
padding-left: 0.5rem;
}
.mt-toot-header-user .custom-emoji,
.mt-toot-text .custom-emoji {
.mt-post-header-user .mt-custom-emoji,
.mt-post-txt .mt-custom-emoji {
height: 1.5rem;
min-width: 1.5rem;
margin-bottom: -0.25rem;
@ -193,80 +201,67 @@ html[data-theme="dark"] {
}
/* Poll */
.mt-toot-poll {
.mt-post-poll {
margin-bottom: 1rem;
color: var(--content-text);
color: var(--mt-color-content-txt);
}
.mt-toot-poll ul {
.mt-post-poll ul {
list-style: none;
padding: 0;
margin: 0;
}
.mt-toot-poll ul li {
.mt-post-poll ul li {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.mt-toot-poll.mt-toot-poll-expired ul li {
color: var(--contrast-gray-color);
.mt-post-poll.mt-post-poll-expired ul li {
color: var(--mt-color-contrast-gray);
}
.mt-toot-poll ul li:not(:last-child) {
.mt-post-poll ul li:not(:last-child) {
margin-bottom: 0.25rem;
}
.mt-toot-poll ul li:before {
.mt-post-poll ul li:before {
content: "◯";
padding-right: 0.5rem;
}
.mt-toot-poll.mt-toot-poll-expired ul li:before {
.mt-post-poll.mt-post-poll-expired ul li:before {
content: "";
padding-right: 0;
}
/* Medias */
.mt-toot-media {
.mt-post-media {
position: relative;
overflow: hidden;
margin-bottom: 1rem;
}
.mt-toot-media > .spoiler-btn {
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
transform: translate(-50%, -50%);
}
.mt-toot-media-spoiler > img,
.mt-toot-media-spoiler > audio,
.mt-toot-media-spoiler > video,
.mt-toot-media-spoiler > .mt-toot-media-play-icon {
.mt-post-media-spoiler > img,
.mt-post-media-spoiler > audio,
.mt-post-media-spoiler > video,
.mt-post-media-spoiler > .mt-post-media-play-icon {
filter: blur(2rem);
pointer-events: none;
}
.mt-toot-media.img-ratio14_7,
.mt-toot-media.video-ratio14_7 {
padding-top: 56.95%;
width: 100%;
}
.mt-toot-media > audio {
.mt-post-media > audio {
width: 100%;
position: relative;
z-index: 1;
}
.img-ratio14_7 > img,
.video-ratio14_7 > img,
.video-ratio14_7 > video {
.mt-post-media > img,
.mt-post-media > video {
width: 100%;
height: auto;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: var(--content-text);
color: var(--mt-color-content-txt);
}
.mt-toot-media.loading-spinner .mt-toot-media-play-icon {
.mt-post-media.mt-loading-spinner .mt-post-media-play-icon {
display: none;
}
.mt-toot-media-play-icon {
.mt-post-media-play-icon {
display: flex;
position: absolute;
width: 3rem;
@ -279,44 +274,44 @@ html[data-theme="dark"] {
border: none;
cursor: pointer;
}
.mt-toot-media-play-icon > svg {
.mt-post-media-play-icon > svg {
width: 2.5rem;
height: 2.5rem;
fill: var(--bg-color);
stroke:var(--content-text);
fill: var(--mt-color-bg);
stroke: var(--mt-color-content-txt);
stroke-width: 1px;
}
/* Preview link */
.mt-toot-preview {
.mt-post-preview {
min-height: 4rem;
display: flex;
flex-direction: row;
border: 1px solid var(--line-gray-color);
border: 1px solid var(--mt-color-line-gray);
border-radius: 0.5rem;
color: var(--link-color);
color: var(--mt-color-link);
font-size: 0.8rem;
margin: 1rem 0;
overflow: hidden;
}
.mt-toot-preview-image {
.mt-post-preview-image {
width: 40%;
align-self: stretch;
}
.mt-toot-preview-image img {
.mt-post-preview-image img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
color: var(--content-text);
color: var(--mt-color-content-txt);
}
.mt-toot-preview-noImage {
.mt-post-preview-noImage {
width: 40%;
font-size: 1.5rem;
align-self: center;
text-align: center;
}
.mt-toot-preview-content {
.mt-post-preview-content {
width: 60%;
display: flex;
align-self: center;
@ -324,59 +319,93 @@ html[data-theme="dark"] {
padding: 0.5rem 1rem;
gap: 0.5rem;
}
.mt-toot-preview-title {
.mt-post-preview-title {
font-weight: 600;
}
/* Spoiler button */
.spoiler-btn {
border-radius: 2px;
background-color: var(--line-gray-color);
border: 0;
color: var(--content-text);
font-weight: 700;
font-size: 0.7rem;
padding: 0 0.35rem;
text-transform: uppercase;
line-height: 1.25rem;
cursor: pointer;
vertical-align: top;
}
/* Counter bar */
.mt-toot-counter-bar {
.mt-post-counter-bar {
display: flex;
min-width: 6rem;
max-width: 40rem;
justify-content: space-between;
color: var(--contrast-gray-color);
color: var(--mt-color-contrast-gray);
}
.mt-toot-counter-bar-replies,
.mt-toot-counter-bar-reblog,
.mt-toot-counter-bar-favorites {
.mt-post-counter-bar-replies,
.mt-post-counter-bar-reblog,
.mt-post-counter-bar-favorites {
display: flex;
font-size: 0.75rem;
gap: 0.25rem;
align-items: center;
opacity: 0.5;
}
.mt-toot-counter-bar-replies > svg,
.mt-toot-counter-bar-reblog > svg,
.mt-toot-counter-bar-favorites > svg {
.mt-post-counter-bar-replies > svg,
.mt-post-counter-bar-reblog > svg,
.mt-post-counter-bar-favorites > svg {
width: 1rem;
fill: var(--contrast-gray-color);
fill: var(--mt-color-contrast-gray);
}
/* Buttons */
.mt-container .mt-btn-dark {
display: flex;
border-radius: 0.25rem;
background-color: var(--mt-color-line-gray);
border: 0;
color: var(--mt-color-content-txt);
font-weight: 600;
font-size: 0.75rem;
text-align: center;
padding: 0 0.5rem;
line-height: 1.25rem;
vertical-align: top;
}
.mt-container .mt-btn-violet,
.mt-container a.mt-btn-violet {
display: flex;
gap: 0.5rem;
border-radius: 0.25rem;
border: 0.5rem;
padding: 0.5rem 0.75rem;
font-size: 1rem;
font-weight: 600;
text-align: center;
background-color: var(--mt-color-btn-bg);
color: var(--mt-color-btn-txt);
}
.mt-container .mt-btn-violet:hover,
.mt-container a.mt-btn-violet:hover {
background-color: var(--mt-color-btn-bg-hover);
text-decoration: none;
}
.mt-post-txt .mt-btn-spoiler {
display: inline-block;
}
.mt-post-media.mt-loading-spinner > .mt-btn-spoiler {
display: none;
}
.mt-post-media > .mt-btn-spoiler {
position: absolute;
top: 50%;
left: 50%;
z-index: 2;
transform: translate(-50%, -50%);
}
/* Error */
.mt-error {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
height: calc(100% - 3.5rem);
width: calc(100% - 4.5rem);
justify-content: center;
align-items: center;
color: var(--error-text-color);
color: var(--mt-color-error-txt);
padding: 0.75rem;
text-align: center;
}
@ -384,14 +413,15 @@ html[data-theme="dark"] {
font-size: 2rem;
}
.mt-error-message {
width: 100%;
padding: 1rem 0;
}
.mt-error-message hr {
color: var(--line-gray-color);
color: var(--mt-color-line-gray);
}
/* Loading spinner */
.mt-body > .loading-spinner {
.mt-body > .mt-loading-spinner {
position: absolute;
width: 3rem;
height: 3rem;
@ -399,7 +429,7 @@ html[data-theme="dark"] {
top: calc(50% - 1.5rem);
right: calc(50% - 1.5rem);
}
.loading-spinner {
.mt-loading-spinner {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'%3E%3Cg%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'/%3E%3Cpath d='M64 6.69a57.3 57.3 0 1 1 0 114.61A57.3 57.3 0 0 1 6.69 64' fill='none' stroke='%23404040' stroke-width='12'/%3E%3C/g%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center center;
@ -407,11 +437,15 @@ html[data-theme="dark"] {
background-size: min(2.5rem, calc(100% - 0.5rem));
}
/* Footer (See more link) */
/* Footer */
.mt-footer {
margin: 1rem auto 2rem auto;
padding: 0 2rem;
text-align: center;
display: flex;
flex-flow: wrap;
margin: auto auto 2rem auto;
padding: 0 1.5rem;
gap: 1.5rem;
align-items: center;
justify-content: center;
}
/* Hidden elements */

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long