WIP: Some visual improvements for login #20
					 7 changed files with 191 additions and 33 deletions
				
			
		
							
								
								
									
										15
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -1095,6 +1095,12 @@
 | 
				
			||||||
        "to-fast-properties": "^2.0.0"
 | 
					        "to-fast-properties": "^2.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "@popperjs/core": {
 | 
				
			||||||
 | 
					      "version": "2.5.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.5.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-RFwCobxsvZ6j7twS7dHIZQZituMIDJJNHS/qY6iuthVebxS3zhRY+jaC2roEKiAYaVuTcGmX6Luc6YBcf6zJVg==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "@types/color-name": {
 | 
					    "@types/color-name": {
 | 
				
			||||||
      "version": "1.1.1",
 | 
					      "version": "1.1.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
 | 
				
			||||||
| 
						 | 
					@ -3832,6 +3838,15 @@
 | 
				
			||||||
        "process": "~0.11.0"
 | 
					        "process": "~0.11.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "tippy.js": {
 | 
				
			||||||
 | 
					      "version": "6.2.7",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.2.7.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-k+kWF9AJz5xLQHBi3K/XlmJiyu+p9gsCyc5qZhxxGaJWIW8SMjw1R+C7saUnP33IM8gUhDA2xX//ejRSwqR0tA==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "requires": {
 | 
				
			||||||
 | 
					        "@popperjs/core": "^2.4.4"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "to-fast-properties": {
 | 
					    "to-fast-properties": {
 | 
				
			||||||
      "version": "2.0.0",
 | 
					      "version": "2.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@
 | 
				
			||||||
    "jshint": "^2.12.0",
 | 
					    "jshint": "^2.12.0",
 | 
				
			||||||
    "node-fetch": "^2.6.0",
 | 
					    "node-fetch": "^2.6.0",
 | 
				
			||||||
    "pug": "^3.0.0",
 | 
					    "pug": "^3.0.0",
 | 
				
			||||||
    "sass": "^1.26.10"
 | 
					    "sass": "^1.26.10",
 | 
				
			||||||
 | 
					    "tippy.js": "^6.2.7"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,36 +1,78 @@
 | 
				
			||||||
 | 
					const tippy = require("tippy.js");
 | 
				
			||||||
const {q, ElemJS, ejs} = require("./basic.js")
 | 
					const {q, ElemJS, ejs} = require("./basic.js")
 | 
				
			||||||
 | 
					const {S_RE_DOMAIN, S_RE_IPV6, S_RE_IPV4} = require("./re.js")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const password = q("#password")
 | 
					const password = q("#password")
 | 
				
			||||||
const homeserver = q("#homeserver")
 | 
					
 | 
				
			||||||
 | 
					// A regex matching a lossy MXID
 | 
				
			||||||
 | 
					// Groups:
 | 
				
			||||||
 | 
					//  1: username/localpart
 | 
				
			||||||
 | 
					//	MAYBE WITH
 | 
				
			||||||
 | 
					//  2: hostname/serverpart
 | 
				
			||||||
 | 
					//		3: domain
 | 
				
			||||||
 | 
					//		OR
 | 
				
			||||||
 | 
					//		4: IP address
 | 
				
			||||||
 | 
					// 			5: IPv4 address
 | 
				
			||||||
 | 
					//			OR
 | 
				
			||||||
 | 
					//			6: IPv6 address
 | 
				
			||||||
 | 
					//		MAYBE WITH
 | 
				
			||||||
 | 
					//		7: port number
 | 
				
			||||||
 | 
					const RE_LOSSY_MXID = new RegExp(`^@?([a-z0-9._=-]+?)(?::((?:(${S_RE_DOMAIN})|((${S_RE_IPV4})|(\\[${S_RE_IPV6}])))(?::(\\d+))?))?$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.RE_LOSSY_MXID = RE_LOSSY_MXID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Username extends ElemJS {
 | 
					class Username extends ElemJS {
 | 
				
			||||||
	constructor() {
 | 
						constructor(homeserver) {
 | 
				
			||||||
		super(q("#username"))
 | 
							super(q("#username"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.homeserver = homeserver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.on("change", this.updateServer.bind(this))
 | 
							this.on("change", this.updateServer.bind(this))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isValid() {
 | 
						isValid() {
 | 
				
			||||||
		return !!this.element.value.match(/^@?[a-z0-9._=\/-]+(?::[a-zA-Z0-9.:\[\]-]+)?$/)
 | 
							return !!this.element.value.match(RE_LOSSY_MXID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getUsername() {
 | 
						getUsername() {
 | 
				
			||||||
		return this.element.value.match(/^@?([a-z0-9._=\/-]+)/)[1]
 | 
							return this.element.value.match(RE_LOSSY_MXID)[1]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	getServer() {
 | 
						getServer() {
 | 
				
			||||||
		const server = this.element.value.match(/^@?[a-z0-9._=\?-]+:([a-zA-Z0-9.:\[\]-]+)$/)
 | 
							const server = this.element.value.match(RE_LOSSY_MXID)
 | 
				
			||||||
		if (server && server[1]) return server[1]
 | 
							if (server && server[2]) return server[2]
 | 
				
			||||||
		else return null
 | 
							else return null
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	updateServer() {
 | 
						updateServer() {
 | 
				
			||||||
		if (!this.isValid()) return
 | 
							if (!this.isValid()) return
 | 
				
			||||||
		if (this.getServer()) homeserver.value = this.getServer()
 | 
							if (this.getServer()) this.homeserver.suggest(this.getServer())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const username = new Username()
 | 
					class Homeserver extends ElemJS {
 | 
				
			||||||
 | 
						constructor() {
 | 
				
			||||||
 | 
							super(q("#homeserver"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.tippy = tippy.default(q(".homeserver-question"), {
 | 
				
			||||||
 | 
								content: q("#homeserver-popup-template").innerHTML,
 | 
				
			||||||
 | 
								allowHTML: true,
 | 
				
			||||||
 | 
								interactive: true,
 | 
				
			||||||
 | 
								interactiveBorder: 10,
 | 
				
			||||||
 | 
								trigger: "focus mouseenter",
 | 
				
			||||||
 | 
								theme: "carbon",
 | 
				
			||||||
 | 
								arrow: tippy.roundArrow,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suggest(value) {
 | 
				
			||||||
 | 
							this.element.placeholder = value
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getServer() {
 | 
				
			||||||
 | 
							return this.element.value || this.element.placeholder;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Feedback extends ElemJS {
 | 
					class Feedback extends ElemJS {
 | 
				
			||||||
	constructor() {
 | 
						constructor() {
 | 
				
			||||||
| 
						 | 
					@ -60,13 +102,14 @@ class Feedback extends ElemJS {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const feedback = new Feedback()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Form extends ElemJS {
 | 
					class Form extends ElemJS {
 | 
				
			||||||
	constructor() {
 | 
						constructor(username, feedback, homeserver) {
 | 
				
			||||||
		super(q("#form"))
 | 
							super(q("#form"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.processing = false
 | 
							this.processing = false
 | 
				
			||||||
 | 
							this.username = username
 | 
				
			||||||
 | 
							this.feedback = feedback
 | 
				
			||||||
 | 
							this.homeserver = homeserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.on("submit", this.submit.bind(this))
 | 
							this.on("submit", this.submit.bind(this))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -74,12 +117,12 @@ class Form extends ElemJS {
 | 
				
			||||||
	async submit() {
 | 
						async submit() {
 | 
				
			||||||
		if (this.processing) return
 | 
							if (this.processing) return
 | 
				
			||||||
		this.processing = true
 | 
							this.processing = true
 | 
				
			||||||
		if (!username.isValid()) return this.cancel("Username is not valid.")
 | 
							if (!this.username.isValid()) return this.cancel("Username is not valid.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Resolve homeserver address
 | 
							// Resolve homeserver address
 | 
				
			||||||
		let domain
 | 
							let domain
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			domain = await this.findHomeserver(homeserver.value)
 | 
								domain = await this.findHomeserver(this.homeserver.getServer())
 | 
				
			||||||
		} catch (e) {
 | 
							} catch (e) {
 | 
				
			||||||
			return this.cancel(e.message)
 | 
								return this.cancel(e.message)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -90,7 +133,7 @@ class Form extends ElemJS {
 | 
				
			||||||
			method: "POST",
 | 
								method: "POST",
 | 
				
			||||||
			body: JSON.stringify({
 | 
								body: JSON.stringify({
 | 
				
			||||||
				type: "m.login.password",
 | 
									type: "m.login.password",
 | 
				
			||||||
				user: username.getUsername(),
 | 
									user: this.username.getUsername(),
 | 
				
			||||||
				password: password.value
 | 
									password: password.value
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}).then(res => res.json())
 | 
							}).then(res => res.json())
 | 
				
			||||||
| 
						 | 
					@ -153,15 +196,21 @@ class Form extends ElemJS {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	status(message) {
 | 
						status(message) {
 | 
				
			||||||
		feedback.setLoading(true)
 | 
							this.feedback.setLoading(true)
 | 
				
			||||||
		feedback.message(message)
 | 
							this.feedback.message(message)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cancel(message) {
 | 
						cancel(message) {
 | 
				
			||||||
		this.processing = false
 | 
							this.processing = false
 | 
				
			||||||
		feedback.setLoading(false)
 | 
							this.feedback.setLoading(false)
 | 
				
			||||||
		feedback.message(message, true)
 | 
							this.feedback.message(message, true)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const form = new Form()
 | 
					const homeserver = new Homeserver()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const username = new Username(homeserver)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const feedback = new Feedback()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const form = new Form(username, feedback, homeserver)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/js/re.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/js/re.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					// A valid internet domain, according to https://stackoverflow.com/a/20046959 (cleaned)
 | 
				
			||||||
 | 
					const S_RE_DOMAIN = "(?:[a-zA-Z]|[a-zA-Z][a-zA-Z]|[a-zA-Z]\\d|\\d[a-zA-Z]|[a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9])\\.(?:[a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\\.[a-zA-Z]{2,3})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A valid ipv4 address, one that doesn't check for valid numbers (e.g. not 999) and one that does
 | 
				
			||||||
 | 
					// const S_RE_IPV4_NO_CHECK = "(?:(?:\\d{1,3}\\.){3}\\d{1,3})"
 | 
				
			||||||
 | 
					const S_RE_IPV4_HAS_CHECK = "(?:(?:25[0-5]|(?:2[0-4]|1{0,1}\\d){0,1}\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}\\d){0,1}\\d)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const S_RE_IPV6_SEG = "[a-fA-F\\d]{1,4}"
 | 
				
			||||||
 | 
					// Yes, this is an ipv6 address.
 | 
				
			||||||
 | 
					const S_RE_IPV6 = `
 | 
				
			||||||
 | 
					(?:
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){7}(?:${S_RE_IPV6_SEG}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){6}(?:${S_RE_IPV4_HAS_CHECK}|:${S_RE_IPV6_SEG}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){5}(?::${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,2}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){4}(?:(?::${S_RE_IPV6_SEG}){0,1}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,3}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){3}(?:(?::${S_RE_IPV6_SEG}){0,2}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,4}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){2}(?:(?::${S_RE_IPV6_SEG}){0,3}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,5}|:)|
 | 
				
			||||||
 | 
					(?:${S_RE_IPV6_SEG}:){1}(?:(?::${S_RE_IPV6_SEG}){0,4}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,6}|:)|
 | 
				
			||||||
 | 
					(?::(?:(?::${S_RE_IPV6_SEG}){0,5}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,7}|:))
 | 
				
			||||||
 | 
					)(?:%[0-9a-zA-Z]{1,})?`
 | 
				
			||||||
 | 
					  .replace(/\s*\/\/.*$/gm, '')
 | 
				
			||||||
 | 
					  .replace(/\n/g, '')
 | 
				
			||||||
 | 
					  .trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RE_DOMAIN_EXACT = new RegExp(`^${S_RE_DOMAIN}$`)
 | 
				
			||||||
 | 
					const RE_IPV4_EXACT = new RegExp(`^${S_RE_IPV4_HAS_CHECK}$`)
 | 
				
			||||||
 | 
					const RE_IPV6_EXACT = new RegExp(`^${S_RE_IPV6}$`)
 | 
				
			||||||
 | 
					const RE_IP_ADDR_EXACT = new RegExp(`^${S_RE_IPV6}|${S_RE_IPV4_HAS_CHECK}$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  S_RE_DOMAIN,
 | 
				
			||||||
 | 
					  S_RE_IPV6,
 | 
				
			||||||
 | 
					  S_RE_IPV4: S_RE_IPV4_HAS_CHECK,
 | 
				
			||||||
 | 
					  RE_DOMAIN_EXACT,
 | 
				
			||||||
 | 
					  RE_IPV4_EXACT,
 | 
				
			||||||
 | 
					  RE_IPV6_EXACT,
 | 
				
			||||||
 | 
					  RE_IP_ADDR_EXACT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -15,18 +15,28 @@ html
 | 
				
			||||||
          .data-input
 | 
					          .data-input
 | 
				
			||||||
            .form-input-container
 | 
					            .form-input-container
 | 
				
			||||||
              label(for="username") Username
 | 
					              label(for="username") Username
 | 
				
			||||||
              input(type="text" name="username" autocomplete="username" placeholder="@username:server.tld" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username
 | 
					              input(type="text" name="username" autocomplete="username" placeholder="@username:server.com" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .form-input-container
 | 
					            .form-input-container
 | 
				
			||||||
              label(for="password") Password
 | 
					              label(for="password") Password
 | 
				
			||||||
              input(name="password" autocomplete="current-password" type="password" required)#password
 | 
					              input(name="password" autocomplete="current-password" type="password" required)#password
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .form-input-container
 | 
					            .form-input-container
 | 
				
			||||||
 | 
					              .homeserver-label
 | 
				
			||||||
                label(for="homeserver") Homeserver
 | 
					                label(for="homeserver") Homeserver
 | 
				
			||||||
              input(type="text" name="homeserver" value="matrix.org" placeholder="matrix.org" required)#homeserver
 | 
					                span.homeserver-question(tabindex=0) (What's this?)
 | 
				
			||||||
 | 
					              input(type="text" name="homeserver" placeholder="matrix.org")#homeserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          #feedback
 | 
					          #feedback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          .form-input-container
 | 
					          .form-input-container
 | 
				
			||||||
            input(type="submit" value="Log in")#submit
 | 
					            input(type="submit" value="Log in")#submit
 | 
				
			||||||
 | 
					    template#homeserver-popup-template
 | 
				
			||||||
 | 
					        .homeserver-popup
 | 
				
			||||||
 | 
					           p
 | 
				
			||||||
 | 
					             | Homeserver is the place where your account lives.
 | 
				
			||||||
 | 
					             | It's usually the website where you registered.
 | 
				
			||||||
 | 
					           p
 | 
				
			||||||
 | 
					             | Need help finding a homeserver? 
 | 
				
			||||||
 | 
					             a(href='#')  Click here 
 | 
				
			||||||
| 
						
							
	
 | 
				|||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
@use "./base"
 | 
					@use "./base"
 | 
				
			||||||
@use "./loading.sass"
 | 
					@use "./loading"
 | 
				
			||||||
@use "./colors.sass" as c
 | 
					@use "./colors" as c
 | 
				
			||||||
 | 
					@use "./tippy"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.main
 | 
					.main
 | 
				
			||||||
| 
						 | 
					@ -43,6 +44,39 @@
 | 
				
			||||||
.form-error
 | 
					.form-error
 | 
				
			||||||
  color: red
 | 
					  color: red
 | 
				
			||||||
| 
						
							
	
 
				
					
						cadence
						commented  
			
		This needs to be a lighter red for better contrast. Try #ff6561 I guess? This needs to be a lighter red for better contrast. Try #ff6561 I guess? 
			
			
		
				
					
						bad
						commented  
			
		I think we should get a color scheme first and then replace it with css(or sass) variables I think we should get a color scheme first and then replace it with css(or sass) variables
https://gitdab.com/cadence/Carbon/issues/27 
			
			
		 | 
				|||||||
 | 
					
 | 
				
			||||||
 | 
					.homeserver-question
 | 
				
			||||||
 | 
					  font-size: 0.8em
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						
							
	
 
				
				bad marked this conversation as resolved
				
			 
			
				
					
						cadence
						commented  
			
		This does nothing, remove it. This does nothing, remove it. 
			
			
		 | 
				|||||||
 | 
					.homeserver-label
 | 
				
			||||||
 | 
					  display: flex
 | 
				
			||||||
 | 
					  justify-content: space-between
 | 
				
			||||||
 | 
					  align-items: flex-end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.homeserver-popup
 | 
				
			||||||
 | 
					  p 
 | 
				
			||||||
 | 
					    margin: 0.2em
 | 
				
			||||||
 | 
					  a
 | 
				
			||||||
 | 
					    &, &:hover, &:active
 | 
				
			||||||
 | 
					      color: white
 | 
				
			||||||
 | 
					      text-decoration: none
 | 
				
			||||||
 | 
					    &:hover
 | 
				
			||||||
 | 
					      color: #f00 //Placeholder 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes spin
 | 
				
			||||||
 | 
					  0%
 | 
				
			||||||
 | 
					    transform: rotate(0deg)
 | 
				
			||||||
 | 
					  100%
 | 
				
			||||||
 | 
					    transform: rotate(180deg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loading-icon
 | 
				
			||||||
 | 
					  display: inline-block
 | 
				
			||||||
 | 
					  background-color: #ccc
 | 
				
			||||||
 | 
					  width: 12px
 | 
				
			||||||
 | 
					  height: 12px
 | 
				
			||||||
 | 
					  margin-right: 6px
 | 
				
			||||||
 | 
					  animation: spin 0.7s infinite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
input, button
 | 
					input, button
 | 
				
			||||||
  font-family: inherit
 | 
					  font-family: inherit
 | 
				
			||||||
| 
						 | 
					@ -67,6 +101,7 @@ button, input[type="submit"]
 | 
				
			||||||
  padding: 7px
 | 
					  padding: 7px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover
 | 
					  &:hover
 | 
				
			||||||
 | 
					    cursor: pointer
 | 
				
			||||||
    background-color: c.$milder
 | 
					    background-color: c.$milder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
label
 | 
					label
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/sass/tippy.sass
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/sass/tippy.sass
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					@use "../../node_modules/tippy.js/dist/tippy.css"
 | 
				
			||||||
 | 
					@use "../../node_modules/tippy.js/dist/svg-arrow.css"
 | 
				
			||||||
 | 
					@use "./colors.sass" as c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.tippy-box[data-theme~="carbon"] 
 | 
				
			||||||
 | 
					  background-color: c.$milder
 | 
				
			||||||
 | 
					  border: 2px solid c.$divider 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .tippy-svg-arrow
 | 
				
			||||||
 | 
					    fill: c.$milder
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	
This link doesn't go anywhere. I assume the site is yet to be created?
Can we make the link look like a link, aka underlined and blue?
Yeah right now we are waiting if someone in the matrix community will make a server picker similiar to joinmastodon.org.
https://github.com/daydream-mx/keymaker looks interesting but if it doesn't get finished we might have to make our own alternative