lexi

i like breaking computers

  • it/its, #[deprecated] she/her
  • ./a.out

i like rust, nix, linux, infosec, webdev and i shitpost a lot. ctf player and so-called "security researcher". aroace, agender, nb, nd, disabled, &, ΘΔ :3 🏳️‍⚧️ 🟨⬜️🟪⬛️

picrew: #1322863


lexi
@lexi
SocialKnife logo
SocialKnife - @lexi on Cohost
close
close
back
back
reload
lock
socialknife.com/cohost/users/@lexi
download
more
SocialKnife logo
SocialKnife
Real Time Cohost Follower Count
avatar
Lexi |
  Cohost logo 
A
 
l
o
t
 
o
f
 
t
h
e
m
A
 
l
o
t
 
o
f
 
t
h
e
m
Refreshing...

lexi
@lexi

SocialKnife.svelte:

<script>
import SocialKnifeContent from "./SocialKnifeContent.svelte";
import SocialKnifeSource from "./SocialKnifeSource.svelte";
</script>

<div class="browser">
<div class="tab-bar">
<div class="tabs">
<div class="pre-tab">
<div></div>
</div>
<div class="tab">
<div class="icon"><img src="https://socialblade.com/favicon.ico" alt="SocialKnife logo"></div>
<div class="text">SocialKnife - @<span>lexi</span> on Cohost</div>
<div class="close btn"><a href="https://cohost.org/lexi">
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a0/OOjs_UI_icon_close.svg" alt="close">
</a></div>
</div>
<div class="post-tab">
<div></div>
</div>
<div class="new btn">
<div><a href="https://web.tabliss.io/" target="_blank">+</a></div>
</div>
<div class="win-buttons">
<div class="btn">
<div class="minimize"></div>
</div>
<div class="btn">
<div class="maximize"></div>
</div>
<div class="btn close-svg"><a href="https://cohost.org/lexi">
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a0/OOjs_UI_icon_close.svg" alt="close">
</a></div>
</div>
</div>
</div>
<div class="url-bar">
<div class="nav-btns">
<div class="btn back-svg">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d9/Feather-arrows-arrow-left.svg" alt="back">
</div>
<div class="btn fwd-svg">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d9/Feather-arrows-arrow-left.svg" alt="back">
</div>
<div class="btn reload"><a href="https://cohost.org/lexi/[recursive]">
<img src="https://upload.wikimedia.org/wikipedia/commons/1/17/OOjs_UI_icon_reload.svg" alt="reload">
</a></div>
</div>
<div class="url-container">
<div class="btn lock">
<img src="https://upload.wikimedia.org/wikipedia/commons/9/95/OOjs_UI_icon_lock.svg" alt="lock">
</div>
<div class="url-text"><span class="black">socialknife.com</span>/cohost/users/@<span>lexi</span></div>
<div class="btn star"></div>
</div>
<div class="other-btns">
<div class="btn download">
<img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/OOjs_UI_icon_download-progressive.svg" alt="download">
</div>
<div class="btn more">
<img src="https://upload.wikimedia.org/wikipedia/commons/0/01/OOjs_UI_icon_ellipsis-progressive.svg" alt="more">
</div>
</div>
</div>
<div class="content">
<SocialKnifeContent/>
</div>
</div>

<br>

<details>
<summary>
<span
style="font-family: League Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, liberation mono, courier new, monospace">Source code</span>
</summary>

<p>SocialKnife.svelte:</p>

<pre>here</pre>

<p>SocialKnifeContent.svelte:</p>

<pre>here</pre>
</details>

<style>
:global(img) {
margin-top: 0;
margin-bottom: 0;
}

.btn {
user-select: none;
cursor: pointer;
}

.tabs {
height: 50px;
background: #444;
position: relative;
display: flex;
color: #fff;
}

.tab {
background: #666;
width: 200px;
height: 40px;
position: relative;
top: 10px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
display: flex;
align-items: center;
}

.tab .icon, .tab .close {
margin: 10px 10px;
font-size: 80%;
}

.tab .icon img {
border-radius: 3px;
width: 25px;
height: 20px;
}

.new {
height: 100%;
font-size: 175%;
position: relative;
top: 4px;
padding-left: 5px;
}

.tab .text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 80%;
font-family: system-ui;
}

.pre-tab, .post-tab {
width: 10px;
height: 50px;
background: #666;

}

.pre-tab div, .post-tab div {
width: 10px;
height: 50px;
background: #444;
}

.pre-tab div {
border-bottom-right-radius: 10px;
}

.post-tab div {
border-bottom-left-radius: 10px;
}

.win-buttons {
align-items: flex-start;
flex: 1 1;
display: flex;
justify-content: flex-end;
padding-top: 5px;
}

.win-buttons > div {
font-size: 125%;
padding-right: 18px;
padding-left: 10px;
}

.url-bar {
background: #666;
height: 50px;
display: flex;
color: #fff;
font-family: system-ui;
align-items: center;
}

.nav-btns, .other-btns {
display: flex;
align-items: center;
}

.nav-btns > div, .other-btns > div {
font-size: 125%;
margin: 5px 10px;
width: 1em;
}

.reload {
font-size: 23px !important;
}

.url-container {
flex: 1 1;
display: flex;
align-items: center;
background: #ddd;
height: 30px;
border-radius: 999px;
margin-left: 5px;
margin-right: 15px;
color: #555;
width: 1px;
}

.url-text {
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

.lock {
filter: grayscale(200%) brightness(0%) invert(1) brightness(30%);
padding-left: 8px;
padding-right: 5px;
}

.star {
font-size: 140%;
padding-left: 5px;
padding-right: 8px;
font-weight: 900;
}

.black {
color: black;
}

.tab-bar a, .url-bar a {
text-decoration: none;
}

.browser {
border-radius: 10px;
border: 1px solid #aaa;
background: #aaa;
box-shadow: 5px 5px 15px #0008;
overflow: hidden;
}

.content {
overflow: hidden;
}

.reload {
filter: invert(1);
transform: scale(1.1);
}

.back-svg {
filter: invert(1);
transform: scale(1.2)
}

.fwd-svg {
filter: invert(1);
transform: scale(1.2) rotate(180deg);
}

.close-svg {
filter: invert(1);
}

.maximize {
width: 15px;
height: 15px;
margin: 4.5px;
border: 2px solid #fff;
}
.minimize {
width: 15px;
height: 10px;
margin: 4.5px;
border-bottom: 2px solid #fff;
}
.close img {
filter: invert(1);
}
.download > img {
filter: brightness(0%) invert(1);
}
.more {
filter: brightness(0%) invert(1);
transform: rotate(90deg);
margin-right: 18px;
}
</style>

SocialKnifeContent.svelte:

<script>
let inAnim = n => `spin 3s ${n}ms ease-in reverse`
let outAnim = n => `spin 3s ${n}ms ease-in normal`
let genStr = (n, index, anim) => {
let parts = []
for (let i = 0; i < n; i++) {
parts.push(`${anim(i*10000 + index * 100)}`)
}
return 'animation: ' + parts.join(', ') + ';'
}
let text = "A lot of them".split('').map(e=>e!==' '?e:`&nbsp;`)
</script>

<div class="header">
<img src="https://socialblade.com/favicon.ico" alt="SocialKnife logo">
<div class="text">Social<span class="bold">Knife</span></div>
<div class="selector">
<div>for</div>
<img src="https://cohost.org/static/7ec6f0f3aef87d734f9b.png" alt="Cohost logo">
<div><a href="https://cohost.org/">Cohost.org</a></div>
</div>
</div>
<div class="subheader">
<div class="text">Real Time Cohost Follower Count</div>
</div>
<div class="main">
<div class="avatar-wrapper">
<img class="avatar" src="https://staging.cohostcdn.org/avatar/4039-94e14b0d-fd35-4321-84ab-bd0df5120821-profile.jpg" alt="avatar">
</div>
<div class="user">
<div class="name">Lexi |</div>&nbsp;
<img class="icon" src="https://cohost.org/static/7ec6f0f3aef87d734f9b.png" alt="Cohost logo" title="Cohost">&nbsp;
<div class="username">
@lexi</div>
</div>
<div class="counter">
<div class="layers">
<div class="out">
{#each text as letter, i}
<div class="letter-container">
<div class="letter" style={genStr(100, i, outAnim)}>{@html letter}</div>
</div>
{/each}
</div>
<div class="in">
{#each text as letter, i}
<div class="letter-container">
<div class="letter" style={genStr(100, i, inAnim)}>{@html letter}</div>
</div>
{/each}
</div>
</div>
</div>
<div class="line"></div>
<div class="refreshing">Refreshing...</div>
</div>


<style>
.refreshing {
position: relative;
top: 1000px;
transform: translateY(-980px);
animation: spin 5s -1s cubic-bezier(0.75, 0, 1, 1) alternate infinite;
}

.line {
width: calc(100% + 2 * 20px);
position: relative;
height: 2px;
top: -50px;
background: #f002;
z-index: 1;
}

.layers {
display: flex;
}
.out {
display: flex;
margin-left: 25%;
}
.in {
display: flex;
margin-left: -50%;
}
.header {
display: flex;
background: #2f2f2f;
color: #fff;
width: 100%;
padding: 20px 50px;
align-items: center;
}
.header > img {
margin: 0;
width: 30px;
height: 30px;
}
.header > .text {
font-size: 20px;
margin-left: 10px;
}
.selector {
margin-left: auto;
display: flex;
align-items: center;
}
.selector > img {
width: 30px;
height: 30px;
margin: 0 5px;
}

.subheader {
padding: 15px 50px;
background: #fff;
}

.main {
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 20px;
/* thin repeating diagonal red lines */
background: repeating-linear-gradient(
-45deg,
#fff 0px,
#fff 5px,
#fee 5px,
#fee 10px
);
}

.main .avatar {
margin: 0;
border: 2px solid #333;
width: 80px;
height: 80px;
}
.avatar-wrapper {
background: #333;

}

.user {
display: flex;
align-items: center;
margin-top: 10px;
}


.icon {
width: 20px;
height: 20px;
margin: 0;
}

.counter {
display: flex;
align-items: center;
font-size: 75px;
overflow: hidden;
/*noinspection CssInvalidPropertyValue*/
-webkit-mask-image: linear-gradient(0deg,
transparent 0%,
black 20%,
black 80%
,transparent 100%
);
z-index: 10;
}

.letter-container {
position: relative;
}


.in .letter {
position: relative;
top: -400px;
left: 0;
transform: translate(0, 400px);
/*animation: spin 2000ms 1000ms cubic-bezier(0.5, 0, 1, 1) reverse;*/
}

.out .letter {
position: relative;
top: 400px;
left: 0;
transform: translate(0, -400px);
/*animation: spin 2000ms 1000ms cubic-bezier(0.5, 0, 1, 1) normal;*/
/*opacity: 0;*/
}

.bold {
font-weight: bold;
}
</style>

You must log in to comment.

in reply to @lexi's post:

this took fucking FOREVER due to a couple of reasons:

  • i wrote a svelte -> cohost transpiler just for this
  • the animation was super hard to pull off only with cohosts limited css
  • looping it was a pain in the ass
  • making every letter offset was also annoying
  • the fade to the bottom was pretty hard to find too
  • making a whole browser UI in css turns out to be hard
  • i originally used unicode text instead of SVG icons but that did not work on android lmao