1 2<!DOCTYPE html> 3<html lang="en" data-navbar-cached="1"> 4<head> 5 <meta name=âfacebook-domain-verificationâ content=âlb07chyng6etwkpmjwu29mkje960mxâ /> 6 <!-- Google Tag Manager --> 7
7<script id="gtmScript" nonce=''>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 8 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 9 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 10 'https://www.googletagmanager.com/gtm.js?id='+i+dl+ '>m_auth=RyNPjkDTh-LgK4oo5gJi6A>m_preview=env-1>m_cookies_win=x'; 11 var n=d.querySelector('[nonce]');n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f); 12 })(window,document,'script','dataLayer','GTM-MKL9HM6');</script>
12 13<!-- End Google Tag Manager --> 14 <meta charset="utf-8"> 15 <meta name="viewport" content="width=device-width, initial-scale=1" /> 16 17 <meta charset="utf-8"> 18 19 <title>Roll20: Online virtual tabletop for pen and paper RPGs and board games </title> 20 21 22 23 24 25 <meta property="og:title" content="Roll20 - Virtual Tabletop for Playing D&D, Pathfinder, Call of Cthulhu, TTRPGs" /><meta property='og:type' content='website' /> 26<meta property='og:url' content='https://roll20.net' /> 27<meta property='og:image' content='images/roll20-home-og.jpg' /> 28<meta property="og:description" content="Easily create characters, organize games, purchase content, find players, and customize your story for your group - all in your browser." /><meta property='og:locale' content='en_US' /> 29<meta property='og:locale:alternate' content='af_ZA' /> 30<meta property='og:locale:alternate' content='ca_CA' /> 31<meta property='og:locale:alternate' content='zh_Hant_TW' /> 32<meta property='og:locale:alternate' content='cs_CZ' /> 33<meta property='og:locale:alternate' content='da_DK' /> 34<meta property='og:locale:alternate' content='nl_NL' /> 35<meta property='og:locale:alternate' content='fr_FR' /> 36<meta property='og:locale:alternate' content='de_DE' /> 37<meta property='og:locale:alternate' content='el_GR' /> 38<meta property='og:locale:alternate' content='he_IL' /> 39<meta property='og:locale:alternate' content='hu_HU' /> 40<meta property='og:locale:alternate' content='it_IT' /> 41<meta property='og:locale:alternate' content='ja_JP' /> 42<meta property='og:locale:alternate' content='ko_KR' /> 43<meta property='og:locale:alternate' content='pl_PL' /> 44<meta property='og:locale:alternate' content='pt_PT' /> 45<meta property='og:locale:alternate' content='ru_RU' /> 46<meta property='og:locale:alternate' content='es_ES' /> 47<meta property='og:locale:alternate' content='sv_SE' /> 48<meta property='og:locale:alternate' content='tr_TR' /> 49<meta property='og:locale:alternate' content='uk_UK' /> 50 51 <meta property="og:site_name" content="Roll20" /> 52 <meta property="og:logo" content="https://roll20.net/images/logo-die-large.png?1790172625" /> 53 <meta property="og:image:alt" content='Roll20 logo, a pink and purple 20-sided die with the number twenty featured on its face. The die sits on top of the name "Roll20" in black font.' /> 54 <meta name="twitter:card" content="summary"/> 55 <meta name="twitter:site" content="@roll20app"/> 56 57 58 59 <meta name="description" content="Play games anywhere. Share them with anyone. With Roll20® as your virtual tabletop, your adventures are limitless."> 60 61 62 <link rel="stylesheet" href="/v3/assets/css/marketplace-v3.css?1790172626" type="text/css" /> 63 64 <link rel="stylesheet" href="/v3/assets/js/libs/@fortawesome/fontawesome-free/css/all.min.css?1790172626" type="text/css" /> 65 <link rel="stylesheet" href="/v2/css/stripe.css?1790172626" type="text/css" /> 66 <link rel="stylesheet" href="/css/fonts.css?1790172625" type="text/css" /> 67 <link rel="stylesheet" href="/v2/css/colors.css?1790172626" type="text/css" /> 68 <link rel="stylesheet" href="/v3/assets/css/homepage-v3.css?1790172626" type="text/css" /> 69 70 71
71<script type="text/javascript" src="/assets/v3.js?1790172626"></script>
71 72 73 74 75
75<script src="https://js.stripe.com/v3/"></script>
75 76
76<script type="text/javascript"> 77 const stripe = Stripe("pk_live_1vH03GH31ZvT2lI7yU8bcClq9jDHOPU4OlnsWqZKu2s4Vs014SXpmjsjnRv37wD8PhLLkYj1YcLSxVDbdzba2HZiQ00qGgUFDqB"); 78 const elements = stripe.elements(); 79 </script>
79 80 81 82 83
83<script type="text/javascript"> 84 85 $('html').attr('data-theme', 'light'); 86 87 </script>
87 88 89 90
90<script type="text/javascript"> 91 const url = new URL(window.location.href); 92 url.pathname = url.pathname.split('/').slice(0, 3).join('/'); // get the first one or two parts of the url path i.e. '/campaigns/search' or '/login' 93 var _conv_page_type = url.toString(); 94 var _conv_category_id = ""; // unused 95 var _conv_category_name = ""; // only used in V2 marketplace 96 var _conv_product_sku = ""; // only used in V2 marketplace 97 var _conv_product_name = ""; // only used in V2 marketplace & characters 98 var _conv_product_price = ""; // only used in V2 marketplace 99 var _conv_customer_id = 'visitor'; 100 var _conv_custom_v1 = 'loggedout'; 101 var _conv_custom_v2 = ""; // unused 102 var _conv_custom_v3 = ""; // unused 103 var _conv_custom_v4 = ""; // unused 104 </script>
104 105 106 107
107<script type="text/javascript" src="//cdn-4.convertexperiments.com/v1/js/10042023-10042444.js" integrity="sha384-gSDcZjQNbDoXzGah4K3Gb02Y6qVZNgNiMUn6LdQ5FCsldO6ne//TUKkgKabzB65Z"></script>
107 108 109 110 111 112 113 <link rel="stylesheet" href="/static-fallback/nav-bar/master/assets/main-DLmYOJ5a.css" /> 114 115 116</head> 117 118<body> 119 <a href="mainContent" class="skip-link">Skip to Main Content</a> 120 <!-- Google Tag Manager (noscript) --> 121 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MKL9HM6>m_auth=RyNPjkDTh-LgK4oo5gJi6A>m_preview=env-1>m_cookies_win=x" 122 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 123<!-- End Google Tag Manager (noscript) --> 124 <style> 125 .bottom-banner { 126 background-color: #fff; 127 position: fixed; 128 bottom: 40px; 129 z-index: 9999; 130 width: calc(100% - 30px); 131 margin-left: 15px; 132 box-shadow: 0px 0px 16px #888888; 133 display: flex; 134 align-items:center; 135 justify-content: space-around; 136 } 137 138 .bottom-banner-css-version-v2{ 139 border-left: 15px solid #ec008c; 140 } 141 .bottom-banner-css-version-v3{ 142 border-left: 15px solid #35c8f5; 143 } 144 .bottom-banner-css-version-v2 .bottom-banner__button{ 145 background-color: #35c8f5; 146 } 147 .bottom-banner-css-version-v3 .bottom-banner__button{ 148 background-color: #ec008c; 149 } 150 151 .bottom-banner--hidden { 152 display:none; 153 } 154 155 .bottom-banner__message { 156 padding: 1em; 157 float: left; 158 font-size: 12px; 159 line-height: 1.4; 160 } 161 162 .bottom-banner__dismiss-icon { 163 float: right; 164 padding:10px; 165 color: #d1d1d1; 166 cursor: pointer; 167 align-self: flex-start; 168 } 169 .bottom-banner__dismiss-icon:hover { 170 color: #333333; 171 } 172 173 .bottom-banner__button, .bottom-banner__button:hover, .bottom-banner__button:visited, .bottom-banner__button:active { 174 float:right; 175 font-size: 16px; 176 line-height: 1.6; 177 border-radius: 4px; 178 padding: 7px 15px 7px 15px; 179 color: #fff !important; 180 transition: all .25s linear; 181 text-decoration: none; 182 } 183 .bottom-banner__button:hover { 184 color: #fff; 185 } 186 187 .cookie-modal--hidden { 188 display: none; 189 position: fixed; 190 z-index: 10001; 191 left: 0; 192 top: 0; 193 width: 100%; 194 height: 100%; 195 overflow: auto; 196 } 197 198 .cookie-modal-content { 199 background-color: var(--color-surface1); 200 margin: 20% auto; 201 padding: 20px; 202 width: 40%; 203 box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.6), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 204 } 205 206 .cookie-modal-content h3 { 207 display: inline-block; 208 } 209 210 .cookie-modal-css-version-v3 { 211 padding-top: 16px; 212 } 213 214 .cookie-modal_switch .switch{ 215 display: inline-block; 216 float: right; 217 margin-top: 30px; 218 width: 60px; 219 } 220 .switch { 221 position: relative; 222 display: inline-block; 223 width: 60px; 224 height: 26px; 225 } 226 227 .switch input { 228 opacity: 0; 229 width: 0; 230 height: 0; 231 } 232 233 .slider { 234 position: absolute; 235 cursor: pointer; 236 top: 0; 237 left: 0; 238 right: 0; 239 bottom: 0; 240 background-color: #8492A6; 241 -webkit-transition: .3s; 242 transition: .3s; 243 } 244 245 .slider:after { 246 content: "Off"; 247 position: absolute; 248 top: 50%; 249 right: 11px; 250 transform: translateY(-50%); 251 color: #fff; 252 font-family: helvetica; 253 font-weight: bold; 254 font-size: 13px; 255 } 256 257 .slider:before { 258 position: absolute; 259 content: ""; 260 height: 20px; 261 width: 20px; 262 left: 3px; 263 bottom: 3px; 264 background-color: white; 265 -webkit-transition: .3s; 266 transition: .3s; 267 } 268 269 input:checked + .slider { 270 background-color: #00853C; 271 } 272 273 input:disabled + .slider { 274 cursor: no-drop; 275 background-color: #C2C7D1; 276 } 277 278 input:disabled + .slider:before { 279 background-color: #DFE2E7; 280 } 281 282 input:focus + .slider { 283 box-shadow: 0 0 5px #2196F3; 284 } 285 286 input:checked + .slider:before { 287 -webkit-transform: translateX(33px); 288 -ms-transform: translateX(33px); 289 transform: translateX(33px); 290 } 291 292 input:checked + .slider:after { 293 content: "On"; 294 right: auto; 295 left: 11px; 296 } 297 298 /* Rounded sliders */ 299 .slider.round { 300 border-radius: 26px; 301 width: 60px; 302 } 303 304 .slider.round:before { 305 border-radius: 50%; 306 } 307 .close { 308 float: right; 309 font-size: 20px; 310 font-weight: bold; 311 line-height: 18px; 312 color: var(--color-grayscale1); 313 text-shadow: 0 1px 0 #ffffff; 314 opacity: 0.2; 315 filter: alpha(opacity=20); 316 } 317 .close:hover { 318 color: var(--color-grayscale1); 319 text-decoration: none; 320 opacity: 0.4; 321 filter: alpha(opacity=40); 322 cursor: pointer; 323 } 324</style> 325 326<!--googleoff: all--> 327<div id="gdpr-notification" class="bottom-banner bottom-banner--hidden bottom-banner-css-version-v3"> 328 <div class="bottom-banner__message"> 329 Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences <a href='#' id='banner-link-preferences'>here</a>. 330 </div> 331 <span class="bottom-banner__dismiss-icon" id="gdpr-notification-dismiss-button" aria-label="Close Cookie Toast" role="button" tabindex="0">×</span> 332</div> 333 334<div id="cookie-modal" class="cookie-modal--hidden"> 335 336 <!-- Modal content --> 337 <div class="cookie-modal-content cookie-modal-css-version-v3"> 338 <span class="close" id="cookie-modal-dismiss">×</span> 339 <h3>Cookie Preferences</h3> 340 <span class="cookie-modal_switch"> 341 <label class="switch"> 342 <span class="sr-only">Accept cookies</span> 343 <input class="feature_toggle" id="cookie-modal-input" type="checkbox"> 344 <span class="slider round"></span> 345 </span> 346 <hr> 347 348 We use Cookies to help personalize and improve Roll20. For more information on our use of non-essential Cookies, visit our Privacy Policy <a href=https://roll20.zendesk.com/hc/articles/360037770793-Terms-of-Service-and-Privacy-Policy target='_blank'>here.</a> 349 </div> 350 351</div> 352<!--googleon: all--> 353
354<script src="https://www.datadoghq-browser-agent.com/datadog-rum.js" type="text/javascript"> 355</script>
vendor: 64 bytes, lines 355-356
355 356<script async src="https://www.googletagmanager.com/gtag/js?id=
356UA-31040388-1
vendor: 12 bytes, line 356
356"></script>
357<script type="text/javascript" nonce=""> 358 // Google analytics function to send events via analytics.js 359 // Remove after everything has been switched to gtag 360 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 361 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 362 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 363 })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 364 365 // Google analytics function to send events via gtag.js 366 function gtag(){dataLayer.push(arguments);} 367 368 initializeOldGoogleAnalytics(); 369 initializeNewGoogleAnalytics(); 370 371 window['ga-disable-UA-31040388-1'] = true; 372 373 var gdprCookie = getCookie("gdpr_accepts_cookies"); 374 375 if (gdprCookie != undefined) { 376 gdprCookie == "true" ? gdprCookie = true : gdprCookie = false; 377 }; 378 379 var gdprBanner = document.getElementById("gdpr-notification"); 380 var gdprBannerDissmissButton = document.getElementById("gdpr-notification-dismiss-button"); 381 var gdprLoggedIn = false; 382 var gdprAccountAcceptsCookies = null; 383 384 var cookieModal = document.getElementById("cookie-modal"); 385 var cookieModalClose = document.getElementById("cookie-modal-dismiss"); 386 var cookieModalSwitch = document.getElementById("cookie-modal-input"); 387 388 var bannerLinkPreferences = document.getElementById("banner-link-preferences"); 389 390 bannerLinkPreferences.onclick = function () { 391 cookieModal.style.display = 'block'; 392 hideGDPRBanner(); 393 } 394 395 function setGDPRCookie(value, expirationInDays) { 396 var date = new Date(); 397 date.setTime(date.getTime() + expirationInDays * 24 * 60 * 60 * 1000); //convert to milleseconds 398 var expires = "expires="+ date.toUTCString(); 399 document.cookie = "gdpr_accepts_cookies=" + value + ";" + expires + ";path=/"+ ";domain=.roll20.net"; 400 } 401 402 function getCookie(cname) { 403 var name = cname + "="; 404 var ca = document.cookie.split(';'); 405 for(var i = 0; i < ca.length; i++) { 406 var c = ca[i]; 407 while (c.charAt(0) == ' ') { 408 c = c.substring(1); 409 } 410 if (c.indexOf(name) == 0) { 411 return c.substring(name.length, c.length); 412 } 413 } 414 return undefined; 415 } 416 417 function checkGdprStatus() { 418 if (gdprLoggedIn == true) { 419 checkAccountGdprCookieSettings(); 420 } else { 421 checkPublicGdprCookieSettings(); 422 } 423 } 424 425 function checkAccountGdprCookieSettings() { 426 var cookieExists = getCookie("gdpr_accepts_cookies"); 427 428 switch(cookieExists){ 429 case "true": 430 acceptCookies(); 431 break 432 case "false": 433 denyCookies(); 434 break 435 case undefined: 436 showGDPRBanner(); 437 acceptCookies(); 438 break 439 } 440 } 441 442 function checkPublicGdprCookieSettings() { 443 console.log('Not logged in - checking cookie GDPR status'); 444 const in_discord = false; 445 if (gdprCookie == undefined) { 446 console.log("No GPDR Cookie Detected"); 447 // If a user is logging in from the discord activity, we want to deny our cookies by default so we don't show the cookie banner. Later, we can figure out a better way to handle instead of denying out of the gate (so we have analytics, for example), but for MVP, this works 448 if(in_discord){ 449 denyCookies(); 450 }else{ 451 showGDPRBanner(); 452 acceptCookies(); 453 } 454 455 } else if (gdprCookie == true){ 456 console.log("GDPR Cookie Detected"); 457 acceptCookies(); 458 } 459 } 460 461 // Options object for google analytics based on the current account 462 function getAnalyticsConfigOptions() { 463 464 return {'cookieDomain': 'auto'} 465 466 } 467 468 // Not impacted by GDPR - just connects our account 469 // Remove after everything has been switched to gtag 470 function initializeOldGoogleAnalytics() { 471 ga('create', 'UA-31040388-1', getAnalyticsConfigOptions()); 472 } 473 474 // Not impacted by GDPR - just connects our account 475 function initializeNewGoogleAnalytics() { 476 window.dataLayer = window.dataLayer || []; 477 478 gtag('js', new Date()); 479 480 // Universal analytics 481 gtag('config', 'UA-31040388-1', { ...getAnalyticsConfigOptions(), send_page_view: false }); 482 // Google Analytics 4 483 gtag('config', 'G-SZLSVQPSWG', { ...getAnalyticsConfigOptions(), send_page_view: false }); 484 } 485 486 function turnOnGoogleAnalytics() { 487 window['ga-disable-UA-31040388-1'] = false;
488 ga('send', 'pageview'); 489 if(window.dataLayer) { 490 dataLayer.push({'event': 'optedIn'}); 491 } 492 } 493 494 var showGDPRBanner = function () { 495 gdprBanner.classList.remove("bottom-banner--hidden"); 496 } 497 498 var hideGDPRBanner = function () { 499 gdprBanner.classList.add("bottom-banner--hidden"); 500 } 501 502 var updateUserAccount = function(account_id, value) { 503 var xhr = new XMLHttpRequest(); 504 // need to get the below working for marketplace, compendium, landing (only works for app) 505 xhr.open("PATCH", '/accounts/'+account_id, true); 506 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 507 var cookieValue = "account[gdpr_accepts_cookies]=" + value; 508 xhr.send(cookieValue); 509 } 510 511 var acceptCookies = function () { 512 setGDPRCookie(true, 365); 513 514 515 turnOnGoogleAnalytics(); 516 turnOnDataDogRUM(); 517 // hideGDPRBanner(); 518 document.getElementById("cookie-modal-input").checked = true; 519 } 520 521 var denyCookies = function () { 522 setGDPRCookie(false, 365); 523 524 } 525 526 var toggleCookie = function () { 527 var switchChecked = document.getElementById("cookie-modal-input").checked; 528 setGDPRCookie(switchChecked, 365); 529 530 531 } 532 533 var showCookieModal = function (){ 534 cookieModal.style.display = "block"; 535 } 536 var closeCookieModal = function (){ 537 cookieModal.style.display = "none"; 538 } 539 540 if (gdprCookie == true || gdprAccountAcceptsCookies == true){ 541 document.getElementById("cookie-modal-input").checked = true; 542 } 543 544 gdprBannerDissmissButton.addEventListener("click", hideGDPRBanner); 545 cookieModalClose.addEventListener("click", closeCookieModal); 546 cookieModalSwitch.addEventListener("click", toggleCookie); 547 548 checkGdprStatus(); 549 550 function vttSampleRate() { 551 const sampleRate = 0; 552 if (sampleRate > 5) return sampleRate; 553 return 5; 554 } 555 556 function experiment({ chatTech }) { 557 const experimentsEnabled =false; 558 return experimentsEnabled && chatTech === 'simple-peer'; 559 } 560 561 function staging({ hostName }) { 562 return hostName === 'roll20staging.net'; 563 } 564 565 function ddSampleRate(opts) { 566 if (experiment(opts)) return 100; 567 return staging(opts) ? 100 : vttSampleRate(); 568 } 569 570 function turnOnDataDogRUM () { 571 var tmplMeta = { 572 chatTech: '', 573 hostName: 'roll20.net', 574 version: '26.9.23', 575 }; 576 var sampleRate = ddSampleRate(tmplMeta); 577 window.DD_RUM && window.DD_RUM.init({ 578 applicationId: '95ddf222-5b92-4a62-803a-185cdd723c8b', 579 clientToken: 'pub3fb1f7cf273c5d345be974e50be0dfda', 580 site: 'datadoghq.com', 581 service:'d20app', 582 sampleRate, 583 trackInteractions: true, 584 version: tmplMeta.version, 585 }); 586 } 587</script>
587 588 589 590
590<script type="text/javascript"> 591 // Sets a browser cookie, but only allows true or false flags 592 function setCookie(name, value, expirationInDays) { 593 let date = new Date(); 594 date.setTime(date.getTime() + expirationInDays * 24 * 60 * 60 * 1000); //convert to milleseconds 595 const expires = `expires=${date.toUTCString()}`; 596 if (value == true || value == false) { 597 document.cookie = `${name}=${value};${expires};path=/;domain=.roll20.net`; 598 } else if (name == "subscriptionGiftPurchaseInfo"){ 599 // it is a gift subscription and we need some actual values 600 let amount = value.substring( 601 value.indexOf("=") + 1, 602 value.lastIndexOf("|") 603 ) 604 let giftToId = value.split('|giftToId=')[1]; 605 document.cookie = `subscriptionGiftPurchaseInfo=true|amount=${amount*100}|giftToId=${giftToId};${expires};path=/;domain=.roll20.net`; 606 }else { 607 return undefined; 608 } 609 } 610 611 // Gets a browser cookie value 612 function getCookie(cname) { 613 let name = cname + '='; 614 let ca = document.cookie.split(';'); 615 for(let i = 0; i < ca.length; i++) { 616 let c = ca[i]; 617 while (c.charAt(0) == ' ') { 618 c = c.substring(1); 619 } 620 if (c.indexOf(name) == 0) { 621 return c.substring(name.length, c.length); 622 } 623 } 624 return undefined; 625 } 626 627 function checkCookieFlag(name) { 628 let flag = getCookie(name); 629 if (flag != undefined) { 630 flag == 'true' ? flag = true : flag = false; 631 } 632 return flag; 633 } 634</script>
634 635 636 637 <!-- some pages use the full width of the page (i.e. Characters) but others are more constrained (i.e. v2 pages), so setting width as "full" or "reg" we can make the navbar span the full width or 1440px based on which page the user is on --> 638<style> 639.reg{ 640 display: flex; 641 justify-content: center; 642} 643.reg .navbar-placeholder{ 644 max-width: 1440px; 645} 646.full .navbar-placeholder{ 647 width: 100%; 648} 649.navbar-placeholder{ 650 background-color: var(--color-navbar-background); 651 height: 60px; 652 width: 100%; 653 display: flex; 654 align-items: center; 655 justify-content: space-between; 656 padding: 0 20px; 657 box-sizing: border-box; 658 box-shadow: 0 4px 2px -2px rgba(31, 41, 55, 0.1) 659} 660.left-side-placeholder{ 661 display: flex; 662 gap: 2rem; 663 align-items: center; 664 flex-direction: row; 665} 666.right-side-placeholder { 667 display: flex; 668 gap: 1rem; 669 align-items: center; 670 flex-direction: row; 671} 672.left-side-placeholder p{ 673 text-decoration: none; 674 display: flex; 675 flex-direction: row; 676 font-size: 18px; 677 position: unset; 678 font-weight: 700; 679 margin-left: 0; 680 color: var(--color-item); 681} 682.left-side-placeholder p { 683 display: flex; 684 align-items: center; 685 padding-top: 20px; 686} 687.arrow-svg { 688 height: 24px; 689} 690.placeholder-subscribe-light{ 691 background: linear-gradient(to left, #CC8900 40%, #664400); 692 border: none !important; 693 text-transform: none; 694 color: var(--color-filled-button-text); 695 border-radius: 8px; 696} 697.placeholder-subscribe-dark{ 698 background: linear-gradient(to left, #CC8900 40%, #664400); 699 border: none !important; 700 text-transform: none; 701 color: var(--color-filled-button-text); 702 border-radius: 8px; 703} 704.navbar-mobile-placeholder{ 705 display: none; 706} 707@media (max-width: 991px) { 708 .navbar-placeholder{ 709 display: none; 710 } 711 .navbar-mobile-placeholder{ 712 display: flex; 713 justify-content: space-between; 714 align-items: center; 715 background-color: var(--color-navbar-background); 716 height: 60px; 717 width: 100%; 718 padding: 0 20px; 719 box-sizing: border-box; 720 box-shadow: 0 4px 2px -2px rgba(31, 41, 55, 0.1) 721 } 722} 723@media (max-width: 1325px){ 724 .left-side-placeholder{ 725 gap: 1rem; 726 } 727} 728@media (max-width: 1255px){ 729 .left-side-placeholder p, .left-side-placeholder .btn{ 730 font-size: 14px; 731 } 732} 733</style> 734 735 736<div id="new-navbar" class="full" data-dark-mode="false" data-subscriber="false" data-role="" data-app-url="https://app.roll20.net" data-marketplace-url="https://marketplace.roll20.net" data-landing-url="https://roll20.net" data-display-name="" data-account-notifications-count="" data-site-notifications-count="0" data-logged-in="false" data-pub-id="false" data-compendium-tools="false" data-env="production" style="width: 100%; position: relative; z-index: 2000;"> 737<!-- The navbar Vue app will mount here. To prevent a flashing of no navbar, a static navbar with no functionality exists in this div and will be replaced upon mount (about a second in, so hopefully the user doesn't notice anything) --> 738 739 <div class="navbar-placeholder"> 740 <div class="left-side-placeholder"> 741 <img src="/images/roll20-logo-light.svg" alt="Roll20 Logo" height="30"> 742 <p>Play Now<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 743 <p>Join a Game<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 744 <p>Characters<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 745 <p>Shop<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 746 <p>Tools<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 747 <p>Compendium<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 748 <p>Community<img src="/images/arrow-light.svg" alt="Arrow" height="30" class="arrow-svg"></p> 749 <button class="btn placeholder-subscribe-light">Subscribe</button> 750 </div> 751 <div class="right-side-placeholder"> 752 <img src="/images/bell-light.svg" alt="Search" class="arrow-svg"> 753 <p>Sign In<img src="/images/arrow-grey-light.svg" alt="Arrow" class="arrow-svg"></p> 754 </div> 755 </div> 756 757 <div class="navbar-mobile-placeholder"> 758 <img src="/images/roll20-logo-light.svg" alt="Roll20 Logo" height="30"> 759 <img src="/images/hamburger-light.svg" alt="Hamburger Menu" height="30"> 760 761 </div> 762 763</div> 764 765 766
767<script type='text/javascript'> 768 Object.defineProperty(window, "LANGUAGE", { value: "" , writable: false }); 769</script>
769 770 771 772 773 774 <div id="mainContent" class="sr-only"></div> 775 776 <section class="hero-section" role="banner" aria-label="#1 Choice for D&D Online Section"> 777 <div class="hero-content"> 778 <div class="hero-text"> 779 <h2 class="header-text">The #1 Choice</h2> 780 <h2 class="header-text">For D&D Online.</h2> 781 <button class="homepage-primary-button" onclick="window.location.href='https://app.roll20.net/create-account'">Create Free Account</button> 782 <div class="hero-list"> 783 <p>Join 15+ million players</p> 784 <ul> 785 <li>Play D&D and 1,200+ other game systems â <b>free</b></li> 786 <li>Biggest library of licensed adventures</li> 787 <li><b>No downloads. No hosting.</b> Just open your browser and play</li> 788 </ul> 789 </div> 790 </div> 791 <div class="hero-image"> 792 <img src="/v3/assets/img/homepage/hero.png" alt="Hero Image" /> 793 </div> 794 </div> 795 <div class="hero-footer"> 796 <div class="footer-logos"> 797 <img src="/v3/assets/img/logos/dungeons-dragons.png" alt="D&D Logo" /> 798 <img src="/v3/assets/img/logos/pathfinder2.png" alt="Pathfinder 2 Logo" /> 799 <img src="/v3/assets/img/logos/call-of-cthulhu.png" alt="Call of Cthulhu Logo" /> 800 <img src="/v3/assets/img/logos/MARRPGP_logo.png" alt="Marvel Multiverse Logo" /> 801 <img src="/v3/assets/img/logos/daggerheart.png" alt="Daggerheart Logo" /> 802 <img src="/v3/assets/img/logos/cyberpunk-logo.png" alt="Cyberpunk Logo" /> 803 <img src="/v3/assets/img/logos/fallout.png" alt="Fallout Logo" /> 804 </div> 805 <div class="footer-text"> 806 <span><b>The most flexible tabletop platform on the web.</b></span> 807 <span>Supporting D&D, Pathfinder, and 1,200+ game systems. 15+ million players worldwide.</span> 808 </div> 809 </div> 810 811</section> 812<div class="hero-wave-bottom zindex-100" data-shape-position="bottom" aria-hidden="true"> 813 <svg viewBox="0 159.693 1440 115" preserveAspectRatio="xMidYMin slice" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 814 <path class="hero-wave-fill"d="M0 274.693h1440v-90c-312.847-25-582.257-20-808.23 35-226.2 55-436.12 50-631.77-10v60z" fill="currentColor" /> 815 </svg> 816</div> 817<div class="mobile-spacer-div"></div> 818<div class="multi-section-div"> 819 <img class="party-group" src="/v3/assets/img/homepage/party-group.png" alt="Party Around Fire Image" /> 820 <section class="adventures-section" role="region" aria-labelledby="adventures-title"> 821 <div class="adventures-text-wrapper"> 822 <h2 id="adventures-title" class="header-text">Start with Ready-to-Play Adventures</h2> 823 <p>Roll20® is the most complete solution for digital play. Access character sheets, tokens, rulebooks, dice, and more - with powerful tools to automate the tedious stuff.</p> 824 <button class="homepage-secondary-button" onclick="window.location.href='https://app.roll20.net/create-account'">Create Free Account</button> 825 </div> 826 </section> 827 828 <section class="vtt-image" role="region" aria-label="VTT Playground Interface with various tools and features"> 829 <div class="hotspot__container"> 830 <div class="hotspot__row row justify-content-between d-none d-lg-flex"> 831 <div id="hotspot__card-one" class="hotspot-card"> 832 <b> Roll Dice & Track Characters</b> 833 <span> Integrated dice, character sheets, and automation </span> 834 </div> 835 <div id="hotspot__card-two" class="hotspot-card"> 836 <b> Organize Your Campaign</b> 837 <span> Pages, folders, and asset management built in </span> 838 </div> 839 </div> 840 <div id="homepage-hotspot__svg-container" class="hotspot__svg-container"> 841 <svg id="hotspot__svg" width="0" height="0" class="d-none d-lg-inline"> 842 <path id="hotspot__path1" d="M0 0" stroke="var(--color-primary4)" fill="none" stroke-width="2px" /> 843 <path id="hotspot__path2" d="M0 0" stroke="var(--color-primary4)" fill="none" stroke-width="2px" /> 844 <path id="hotspot__path3" d="M0 0" stroke="var(--color-primary4)" fill="none" stroke-width="2px" /> 845 <path id="hotspot__path4" d="M0 0" stroke="var(--color-primary4)" fill="none" stroke-width="2px" /> 846 <path id="hotspot__path5" d="M0 0" stroke="var(--color-primary4)" fill="none" stroke-width="2px" /> 847 </svg> 848 </div> 849 <div class="hotspot__dots-wrapper mt-5">
850 <span id="dot-one" class="homepage-hotspot__dot dot-one"><span class="d-block d-lg-none">1</span></span> 851 <span id="dot-two" class="homepage-hotspot__dot dot-two"><span class="d-block d-lg-none">2</span></span> 852 <span id="dot-three" class="homepage-hotspot__dot dot-three"><span class="d-block d-lg-none">2</span></span> 853 <span id="dot-four" class="homepage-hotspot__dot dot-four"><span class="d-block d-lg-none">3</span></span> 854 <span id="dot-five" class="homepage-hotspot__dot dot-five"><span class="d-block d-lg-none">4</span></span> 855 <div class="hotspot__image shadow-lg"> 856 <img sizes="100vw" src="/v3/assets/img/homepage/vtt-playground.png" alt="VTT Playground Image" class="img-fluid" /> 857 </div> 858 </div> 859 <div class="hotspot__row row mt-5 justify-content-between d-none d-lg-flex"> 860 <div id="hotspot__card-three" class="hotspot-card"> 861 <b> Dynamic Lighting & Fog of War</b> 862 <span> Reveal maps as players explore (immersion advantage) </span> 863 </div> 864 <div id="hotspot__card-four" class="hotspot-card"> 865 <b> Free Mapmaking with Dungeon Scrawl</b> 866 <span> Create maps in seconds with integrated tools </span> 867 </div> 868 </div> 869 <div class="hotspot__mobile-row row d-flex d-lg-none mt-5"> 870 <div class="col"> 871 <ul class="list-group"> 872 <li class="list-group-item d-flex justify-content-between align-items-center"> 873 <div class="d-flex align-items-center"> 874 <span><b> Roll Dice & Track Characters</b> - Integrated dice, character sheets, and automation </span> 875 </div> 876 <span class="badge badge-primary badge-circle">1</span> 877 </li> 878 <li class="list-group-item d-flex justify-content-between align-items-center"> 879 <div class="d-flex align-items-center"> 880 <span><b> Organize Your Campaign</b> - Pages, folders, and asset management built in </span> 881 </div> 882 <span class="badge badge-primary badge-circle">2</span> 883 </li> 884 <li class="list-group-item d-flex justify-content-between align-items-center"> 885 <div class="d-flex align-items-center"> 886 <span><b> Dynamic Lighting & Fog of War</b> - Reveal maps as players explore (immersion advantage) </span> 887 </div> 888 <span class="badge badge-primary badge-circle">3</span> 889 </li> 890 <li class="list-group-item d-flex justify-content-between align-items-center"> 891 <div class="d-flex align-items-center"> 892 <span><b> Free Mapmaking with Dungeon Scrawl</b> - Create maps in seconds with integrated tools </span> 893 </div> 894 <span class="badge badge-primary badge-circle">4</span> 895 </li> 896 </ul> 897 </div> 898 </div> 899 </div> 900 </section> 901 902 <section class="steps-section" role="region" aria-label="Steps to Get Started"> 903 <div class="steps-box-wrapper"> 904 <div class="steps-box-div"> 905 <img src="/v3/assets/img/homepage/daggerheart-turtle.png" alt="Daggerheart Character 1" class="steps-daggerheart-1-image" aria-hidden="true"/> 906 907 <img src="/v3/assets/img/homepage/roll20-logo.png" alt="Roll20 Logo" class="steps-icon"/> 908 <h3><b>Sign Up Free</b></h3> 909 <p>Start today, no credit card. Free tier available forever, whether you're a GM or player.</p> 910 </div> 911 <div class="steps-box-div"> 912 <img src="/v3/assets/img/homepage/choose-game-icon.png" alt="Choose Game Icon" class="steps-icon"/> 913 <h3><b>Pick your Game</b></h3> 914 <p>D&D, Pathfinder, Call of Cthulhu, or 1,200+ others. Hundreds of free adventures available.</p> 915 </div> 916 <div class="steps-box-div"> 917 <img src="/v3/assets/img/homepage/invite-friends-icon.png" alt="Invites Friends Icon" class="steps-icon"/> 918 <h3><b>Find your Party</b></h3> 919 <p>Invite friends with a link, or find a group on our LFG tool and Discord. Solo? We've got options for you too.</p> 920 </div> 921 <div class="steps-box-div"> 922 <img src="/v3/assets/img/homepage/play-icon.png" alt="Play Icon" class="steps-icon"/> 923 <h3><b>Play</b></h3> 924 <p>That's it. No downloads, no hosting, no setup. Just click and play. New to TTRPGs? Check out our tutorials.</p> 925 926 <img src="/v3/assets/img/homepage/daggerheart-wolf-parent.png" alt="Daggerheart Character 2" class="steps-daggerheart-2-image" aria-hidden="true"/> 927 </div> 928 </div> 929 <button class="homepage-primary-button steps-button" onclick="window.location.href='https://app.roll20.net/create-account'">Create Free Account</button> 930 </section> 931 932 <section class="testimonials-section" role="region" aria-labelledby="testimonials-title"> 933 <div class="testimonials-wrapper"> 934 <h2 id="testimonials-title" class="header-text">User Testimonials</h2> 935 <div class="owl-carousel owl-theme"> 936 <div class="item"> 937 <div class="carousel-img-wrapper"> 938 <img src="/v3/assets/img/homepage/testimonial-1.jpeg" alt="User Testimonial 1" /> 939 </div> 940 <img src="/v3/assets/img/homepage/testimonial-avatar-1.png" alt="User Testimonial Avatar 1" class="testimonial-avatar avatar-right"/> 941 <p class="testimonial-text">"I met most of my friends online. Roll20 keeps our friendships going and keeps us connected in good times and bad."</p> 942 <p class="testimonial-name"><b>James R.</b></p> 943 </div> 944 <div class="item"> 945 <div class="carousel-img-wrapper"> 946 <img src="/v3/assets/img/homepage/testimonial-2.jpeg" alt="User Testimonial 2" /> 947 </div> 948 <img src="/v3/assets/img/homepage/testimonial-avatar-2.png" alt="User Testimonial Avatar 2" class="testimonial-avatar avatar-left"/> 949 <p class="testimonial-text">"Roll20 is the easiest and most accessible way to get started in the VTT space, and you won't regret it."</p> 950 <p class="testimonial-name"><b>Calbeb P.</b></p> 951 </div> 952 <div class="item"> 953 <div class="carousel-img-wrapper"> 954 <img src="/v3/assets/img/homepage/testimonial-3.jpeg" alt="User Testimonial 3" /> 955 </div> 956 <img src="/v3/assets/img/homepage/testimonial-avatar-3.png" alt="User Testimonial Avatar 3" class="testimonial-avatar avatar-right"/> 957 <p class="testimonial-text">"Roll20 helps break down all the stuff that seems complicated about TTRPGs."</p> 958 <p class="testimonial-name"><b>Mário R.</b></p> 959 </div> 960 <div class="item"> 961 <div class="carousel-img-wrapper"> 962 <img src="/v3/assets/img/homepage/testimonial-4.jpeg" alt="User Testimonial 4" /> 963 </div> 964 <img src="/v3/assets/img/homepage/testimonial-avatar-4.png" alt="User Testimonial Avatar 4" class="testimonial-avatar avatar-left"/> 965 <p class="testimonial-text">"I love the ability to create stories and memories and laughs with my friends from all around the world!"</p> 966 <p class="testimonial-name"><b>Allison M.</b></p> 967 </div> 968 <div class="item"> 969 <div class="carousel-img-wrapper"> 970 <img src="/v3/assets/img/homepage/testimonial-5.jpeg" alt="User Testimonial 5" /> 971 </div> 972 <img src="/v3/assets/img/homepage/testimonial-avatar-5.png" alt="User Testimonial Avatar 5" class="testimonial-avatar avatar-right"/> 973 <p class="testimonial-text">"I love the freedom to create and play however and whatever I want while turning it into a great time with my party."</p> 974 <p class="testimonial-name"><b>Maya O.</b></p> 975 </div> 976 </div> 977 </div> 978 </section> 979</div> 980 981<section class="customize-section" role="region" aria-labelledby="customize-title"> 982 <img src="/v3/assets/img/homepage/battle-box.png" alt="Battle Box Image" class="customize-battle-box-image" aria-hidden="true"/> 983 984 <div class="customize-content-wrapper"> 985 <h3 id="customize-title" class="header-text">Customize Your Game</h3> 986 <div class="customize-text-img-wrapper"> 987 <img src="/v3/assets/img/homepage/Macro_Customize.webp" alt="Customize Illustration Image" class="customize-illustration-image" aria-hidden="true"/> 988 <div class="customize-text-wrapper"> 989 <div class="customize-cards-wrapper"> 990 <div class="customize-card"> 991 <div class="customize-card-header"> 992 <img src="/v3/assets/img/homepage/dice-icon.png" alt="Dice Icon Image" class="customize-icon" aria-hidden="true"/> 993 <h4 class="header-text"><b>Automate Rolls & Actions</b></h4> 994 </div> 995 <p>Save time with macros. One click for complex rolls, spell effects, and character actions. Works for any game system.</p> 996 </div> 997 <div class="customize-card"> 998 <div class="customize-card-header"> 999 <img src="/v3/assets/img/homepage/sheet-icon.png" alt="Sheet Icon Image" class="customize-icon" aria-hidden="true"/> 1000 <h4 class="header-text"><b>
1000Character Sheets for Every System</b></h4> 1001 </div> 1002 <p>Built-in sheets for 250+ game systems including D&D, Pathfinder, and more. Or design your own.</p> 1003 </div> 1004 <div class="customize-card"> 1005 <div class="customize-card-header"> 1006 <img src="/v3/assets/img/homepage/api-icon.png" alt="API Icon Image" class="customize-icon" aria-hidden="true"/> 1007 1008 <h4 class="header-text"><b>Advanced Customization (Optional)</b></h4> 1009 </div> 1010 <p>For power users: Full API to extend Roll20. Add new features, automate campaigns, and more. Pro subscription required.</p> 1011 </div> 1012 <div class="customize-card"> 1013 <div class="customize-card-header"> 1014 <img src="/v3/assets/img/homepage/ds-icon.png" alt="Battle Box Image" class="customize-icon" aria-hidden="true"/> 1015 <h4 class="header-text"><b>Dungeon Scrawl Free Map Creation with Dungeon Scrawl</b></h4> 1016 </div> 1017 <p>Create gorgeous maps in minutes with our integrated Dungeon Scrawl tool. No art skills needed.</p> 1018 </div> 1019 </div> 1020 </div> 1021 </div> 1022 <button class="homepage-secondary-button customize-button" onclick="window.location.href='https://app.roll20.net/create-account'">Create Free Account</button> 1023 1024 </div> 1025</section> 1026 1027<section class="community-section" role="region" aria-labelledby="community-title"> 1028 <div class="community-content-div"> 1029 <h2 id="community-title-mobile" class="header-text">Join the Community</h2> 1030 <div class="community-illustration-wrapper"> 1031 <img src="/v3/assets/img/homepage/community.png" alt="Community Illustration Image" class="community-illustration-image" aria-hidden="true"/> 1032 </div> 1033 <div class="community-content-wrapper"> 1034 <div class="community-text-wrapper"> 1035 <h2 id="community-title" class="header-text">Join the Community</h2> 1036 <p class="community-subtitle"><b>Connect with 15+ million players worldwide.</b></p> 1037 <p class="community-text">Find groups on our LFG tool, join the official Discord with 100K+ members, or explore forums and subreddits. New players welcome.</p> 1038 </div> 1039 1040 <div class="community-action-items-wrapper"> 1041 <button class="community-visit external" data-url="https://discord.com/invite/roll20" aria-label="Visit Roll20 Discord"> 1042 <img src="/v3/assets/img/homepage/discord-icon.png" aria-hidden="true" /> 1043 Join Roll20 Discord 1044 <img src="/v3/assets/img/homepage/arrow-shape.png" aria-hidden="true" /> 1045 </button> 1046 <button class="community-visit" onclick="window.location.href='https://app.roll20.net/lfg/search/'" aria-label="Visit Roll20 Looking For Group"> 1047 <img src="/v3/assets/img/homepage/friends-icon.png" aria-hidden="true" /> 1048 Find a Group 1049 <img src="/v3/assets/img/homepage/arrow-shape.png" aria-hidden="true" /> 1050 </button> 1051 <button class="community-visit" onclick="window.location.href='https://app.roll20.net/forum'" aria-label="Visit Roll20 Forums"> 1052 <img src="/v3/assets/img/homepage/roll20-icon.png" aria-hidden="true" /> 1053 Browse Forums 1054 <img src="/v3/assets/img/homepage/arrow-shape.png" aria-hidden="true" /> 1055 </button> 1056 <button class="community-visit external" data-url="https://www.reddit.com/r/Roll20" aria-label="Visit Roll20 Reddit"> 1057 <img src="/v3/assets/img/homepage/reddit-icon.png" aria-hidden="true" /> 1058 Visit r/Roll20 1059 <img src="/v3/assets/img/homepage/arrow-shape.png" aria-hidden="true" /> 1060 </button> 1061 </div> 1062 </div> 1063 </div> 1064</section> 1065 1066 1067<section class="articles-section" role="region" aria-label="start-here-articles"> 1068 <h2 id="articles-title" class="header-text">New to Tabletop Roleplaying Games? Start Here</h2> 1069 <div class="articles-wrapper"> 1070 <div class="article-card" data-url="https://pages.roll20.net/-dnd-starter-sets-complete-guide"> 1071 <h3 class="header-text"><b>Is D&D For Me?</b></h3> 1072 <img src="/v3/assets/img/homepage/article-1.webp" alt="Article 1 Image" class="article-image" /> 1073 <div class="article-content"> 1074 <p>This article demonstrates that D&D is for everyone, dispels myths about time commitment myths, and offers guidance on how to start.</p> 1075 <a class="external-article" href="https://pages.roll20.net/-dnd-starter-sets-complete-guide" target="_blank" rel="noopener noreferrer">Read more</a> 1076 </div> 1077 </div> 1078 <div class="article-card" data-url="https://help.roll20.net/hc/en-us/articles/360037774473-Using-Our-Looking-for-Group-Tool"> 1079 <h3 class="header-text"><b>How to Find Your First Group</b></h3> 1080 1081 <img src="/v3/assets/img/homepage/FindaGroup.png" alt="Article 2 Image" class="article-image" /> 1082 <div class="article-content"> 1083 <p>Explore the guide to using LFG tool, Discord, forums, what to look for in beginner-friendly groups.</p> 1084 <a class="external-article" href="https://help.roll20.net/hc/en-us/articles/360037774473-Using-Our-Looking-for-Group-Tool" target="_blank" rel="noopener noreferrer">Read more</a> 1085 </div> 1086 </div> 1087 <div class="article-card" data-url="https://pages.roll20.net/roll20-101"> 1088 <h3 class="header-text"><b>Roll20 Masterclass Series</b></h3> 1089 <img src="/v3/assets/img/homepage/article-3.webp" alt="Article 3 Image" class="article-image" /> 1090 <div class="article-content"> 1091 <p>Discover GM tips, player tips and feature tutorials.</p> 1092 <a class="external-article" href="https://pages.roll20.net/roll20-101" target="_blank" rel="noopener noreferrer">View Series</a> 1093 </div> 1094 </div> 1095 </div> 1096</section> 1097 1098<section class="blog-section" role="region" aria-labelledby="latest-news-title"> 1099 <h2 id="latest-news-title" class="header-text">Latest News</h2> 1100 <div class="blog-articles-wrapper"> 1101 1102 <div class="blog-article-0 blog-article-card"> 1103 <img src="/v3/assets/img/backgrounds/signup-bg.svg" alt="Blog Article 1 Image" class="blog-article-0-image" /> 1104 <div class="blog-article-0-content"> 1105 <h3 class="blog-article-0-title">What's New...</h3> 1106 <p class="blog-article-0-date">- - -</p> 1107 </div> 1108 </div> 1109 <div class="blog-article-1 blog-article-card"> 1110 <img src="/v3/assets/img/backgrounds/signup-bg.svg" alt="Blog Article 2 Image" class="blog-article-1-image" /> 1111 <div class="blog-article-1-content"> 1112 <h3 class="blog-article-1-title">Checking...</h3> 1113 <p class="blog-article-1-date">- - -</p> 1114 </div> 1115 </div> 1116 <div class="blog-article-2 blog-article-card"> 1117 <img src="/v3/assets/img/backgrounds/signup-bg.svg" alt="Blog Article 3 Image" class="blog-article-2-image" /> 1118 <div class="blog-article-2-content"> 1119 <h3 class="blog-article-2-title">Taking a look...</h3> 1120 <p class="blog-article-2-date">- - -</p> 1121 </div> 1122 </div> 1123 </div> 1124 <div class="blog-footer"> 1125 <button class="homepage-secondary-button blog-button" data-url="https://blog.roll20.net/">Roll20 Blog</button> 1126 <h3><b>Ready to start your adventure?</b></h3> 1127 <button class="homepage-primary-button" onclick="window.location.href='https://app.roll20.net/create-account'">Sign Up Free</button> 1128 <p>No Credit Card Required</p> 1129 <p class="special-thanks-text"><span>Special thanks to our featured Partner:</span> <a href="https://marketplace.roll20.net/browse/publisher/1634" target="_blank">Ori the Cartographer</a></p> 1130 </div> 1131</section> 1132
1133<script type="text/javascript"> 1134// Below JS controls the drawing of elbowed lines from dots to hotspot cards 1135 1136// Each hotspot object defines how to connect a dot to a box 1137 // dotId is the id of the dot element 1138 // boxId is the id of the box element 1139 // elbowDistance controls how far away from the dot the elbow bend occurs 1140 // elbowDirection controls whether the elbow goes out horizontally first, or vertically first 1141 // boxSide controls which side of the box the line connects to 1142 // dotSide controls which side of the dot the line starts from 1143const hotspots = [ 1144 { dotId: '#dot-one', boxId: '#hotspot__card-one', elbowDistance: 50, elbowDirection: 'horizontal', boxSide: 'bottom', dotSide: 'right'}, 1145 { dotId: '#dot-two', boxId: '#hotspot__card-two', elbowDistance: 100, elbowDirection: 'vertical', boxSide: 'left', dotSide: 'top' }, 1146 { dotId: '#dot-three', boxId: '#hotspot__card-two', elbowDistance: 125, elbowDirection: 'horizontal', boxSide: 'bottom', dotSide: 'right' }, 1147 { dotId: '#dot-four', boxId: '#hotspot__card-three', elbowDistance: 40, elbowDirection: 'horizontal', boxSide: 'top', dotSide: 'right' }, 1148 { dotId: '#dot-five', boxId: '#hotspot__card-four', elbowDistance: 150, elbowDirection: 'vertical', boxSide: 'left', dotSide: 'bottom' }, 1149]; 1150 1151function drawElbowPath(svg, path, startX, startY, endX, endY, hotspot) { 1152 // Ensure SVG is big enough 1153 const stroke = parseFloat(path.attr("stroke-width") || 2); 1154 svg.attr("width", Math.max(svg.attr("width") || 0, startX, endX)); 1155 svg.attr("height", Math.max(svg.attr("height") || 0, startY, endY)); 1156 1157 let cornerX = startX; 1158 let cornerY = startY; 1159 1160 // Determine elbow point using direction and distance 1161 if (hotspot.elbowDirection === "horizontal") { 1162 cornerX += (endX >= startX ? 1 : -1) * hotspot.elbowDistance; 1163 } else if (hotspot.elbowDirection === "vertical") { 1164 cornerY += Math.sign(endY - startY) * hotspot.elbowDistance; 1165 } 1166 1167 // Draw path: start â corner â end 1168 if (hotspot.elbowDirection === "horizontal") { 1169 path.attr("d", `M${startX} ${startY} L${cornerX} ${cornerY} L${cornerX} ${endY}`); 1170 } else { 1171 path.attr("d", `M${startX} ${startY} L${cornerX} ${cornerY} L${endX} ${cornerY}`); 1172 } 1173} 1174 1175function connectHotspot(svg, path, hotspot) { 1176 const startElem = $(hotspot.dotId); 1177 const endElem = $(hotspot.boxId); 1178 1179 if (!startElem.length || !endElem.length) return; 1180 1181 const svgContainer = $("#homepage-hotspot__svg-container"); 1182 const svgTop = svgContainer.offset().top; 1183 const svgLeft = svgContainer.offset().left; 1184 1185 const startCoord = startElem.offset(); 1186 const endCoord = endElem.offset(); 1187 1188 // Determine startX/startY based on dotSide 1189 let startX, startY; 1190 switch (hotspot.dotSide) { 1191 // NOTE: "left" does not work if it goes off the left of the image, if the dot is on the left of the image. It will work if the dot is further right. 1192 case 'left': 1193 startX = startCoord.left - svgLeft; 1194 startY = startCoord.top + startElem.outerHeight() / 2 - svgTop; 1195 break; 1196 case 'right': 1197 startX = startCoord.left + startElem.outerWidth() - svgLeft; // right edge 1198 startY = startCoord.top + startElem.outerHeight() / 2 - svgTop; 1199 break; 1200 case 'top': 1201 startX = startCoord.left + startElem.outerWidth() / 2 - svgLeft; 1202 startY = startCoord.top - svgTop; 1203 break; 1204 case 'bottom': 1205 startX = startCoord.left + startElem.outerWidth() / 2 - svgLeft; 1206 startY = startCoord.top + startElem.outerHeight() - svgTop; 1207 break; 1208 default: 1209 startX = startCoord.left + startElem.outerWidth() / 2 - svgLeft; 1210 startY = startCoord.top + startElem.outerHeight() / 2 - svgTop; 1211 } 1212 1213 1214 // Determine endX/endY of the text box based on boxSide 1215 let endX, endY; 1216 switch (hotspot.boxSide) { 1217 case 'left': 1218 endX = endCoord.left - svgLeft; 1219 endY = endCoord.top + endElem.outerHeight() / 2 - svgTop; 1220 break; 1221 case 'right': 1222 endX = endCoord.left + endElem.outerWidth() - svgLeft; 1223 endY = endCoord.top + endElem.outerHeight() / 2 - svgTop; 1224 break; 1225 case 'top': 1226 endX = endCoord.left + endElem.outerWidth() / 2 - svgLeft; 1227 endY = endCoord.top - svgTop; 1228 break; 1229 case 'bottom': 1230 endX = endCoord.left + endElem.outerWidth() / 2 - svgLeft; 1231 endY = endCoord.top + endElem.outerHeight() - svgTop; 1232 break; 1233 default: 1234 endX = endCoord.left + endElem.outerWidth() / 2 - svgLeft; 1235 endY = endCoord.top + endElem.outerHeight() / 2 - svgTop; 1236 } 1237 1238 drawElbowPath(svg, path, startX, startY, endX, endY, hotspot); 1239} 1240 1241function connectAllHotspots() { 1242 const svg = $("#hotspot__svg"); 1243 svg.attr("width", 0).attr("height", 0);
1244 hotspots.forEach((hotspot, i) => { 1245 const pathId = `#hotspot__path${i + 1}`; 1246 const path = $(pathId); 1247 connectHotspot(svg, path, hotspot); 1248 }); 1249} 1250 1251function resetAndConnect() { 1252 const svg = $("#hotspot__svg"); 1253 svg.attr("height", "0"); 1254 svg.attr("width", "0"); 1255 connectAllHotspots(); 1256} 1257 1258// This will wait until all images are loaded to ensure correct positions 1259$(window).on('load', resetAndConnect); 1260$(window).resize(resetAndConnect); 1261 1262$('.owl-carousel').owlCarousel({ 1263 loop: true, 1264 nav: true, 1265 dots: false, 1266 items: 1, 1267 margin: 0, 1268 navText: ['<span class="custom-prev"><img src="/v3/assets/img/homepage/chevron.svg" alt="Previous"></span>', '<span class="custom-next"><img src="/v3/assets/img/homepage/chevron.svg" alt="Next"></span>'], 1269 responsive: { 1270 1220: { 1271 items: 2 1272 } 1273 } 1274}); 1275 1276$('.community-visit.external, .article-card, .blog-button, .blog-article-card').on('click', function() { 1277 const url = $(this).data('url'); 1278 window.open(url, '_blank'); 1279}); 1280 1281async function getBlogData() { 1282 const url = "https://blog.roll20.net/wp-json/wp/v2/posts?status=publish&per_page=3"; 1283 try { 1284 const response = await fetch(url, 1285 ); 1286 if (!response.ok) { 1287 throw new Error(`Response status: ${response.status}`); 1288 } 1289 const result = await response.json(); 1290 if (result){ 1291 result.forEach((post, i) => { 1292 console.log(post) 1293 const postLink = post.link; 1294 const imageSrc = post.jetpack_featured_media_url;
1295 const postTitle = post.title.rendered; 1296 const postDate = new Date(post.date); 1297 const formattedDate = postDate.toLocaleDateString('en-US', { 1298 year: 'numeric', 1299 month: 'short' 1300 }); 1301 $(`.blog-article-${i}`).attr('data-url', postLink); 1302 $(`.blog-article-${i}-image`).attr('src', imageSrc); 1303 $(`.blog-article-${i}-title`).html(postTitle); 1304 $(`.blog-article-${i}-date`).html(formattedDate); 1305 1306 }); 1307 } 1308 } catch (error) { 1309 console.error(error.message); 1310 } 1311} 1312$(document).ready(function() { 1313 getBlogData(); 1314}); 1315 1316</script>
1316 1317 1318 <footer id="footer" class="container globalfooter"> 1319 <div class="footer-terms"> 1320 1321 <a href="https://roll20.net/" target="_blank"><img src="/v3/assets/img/logos/roll20-logo-new-light.svg" alt="Go to Roll20 Homepage"/></a> 1322 1323 <span>© Roll20, LLC | <a href="https://help.roll20.net/hc/articles/360037770793-Terms-of-Service-and-Privacy-Policy#Notification_of_Copyright_Infringement_.28DMCA_Policy.29" target="_blank">Terms of Service & Privacy Policy</a> | <a href="https://help.roll20.net/hc/articles/360037770833-DMCA" target="_blank">DMCA</a> | <a href="https://roll20.zendesk.com/hc/requests/new " target="_blank">Contact Us</a> | <a href="https://roll20.net/help" target="_blank">Help Center</a> | All rights reserved. All product and company names are trademarks<sup>™</sup> or registered<sup>®</sup> trademarks of their respective holders.</span> 1324 1325 <div class="socials"> 1326 <a href="http://discord.gg/roll20" target="_blank"><img src="/v2/images/Discord-LightMode.png" alt="Join the Roll20 Discord Server" /></a> 1327 <a href="https://www.facebook.com/roll20app/" target="_blank"><img src="/v2/images/Icon-Facebook.png" alt="Go to Roll20 on Facebook" /></a> 1328 <a href="https://x.com/roll20app" target="_blank"><img src="/v2/images/Icon-X.png" alt="Go to Roll20 on Twitter" /></a> 1329 <a href="https://bsky.app/profile/roll20.net" target="_blank"><img src="/v2/images/Icon-Bluesky.png" alt="Go to Roll20 on Bluesky" /></a> 1330 <a href="https://www.instagram.com/roll20app/" target="_blank"><img src="/v2/images/Icon-Instagram.png" alt="Go to Roll20 on Instagram" /></a> 1331 <a href=" https://www.tiktok.com/@roll20app?lang=en" target="_blank"><img src="/v2/images/Icon-TikTok.png" alt="Go to Roll20 on TikTok" style="max-height: 41px;"/></a> 1332 <a href="https://www.youtube.com/roll20app" target="_blank"><img src="/v2/images/Icon-YouTube.png" alt="Go to Roll20 on YouTube" /></a> 1333 <a href="https://www.twitch.tv/roll20app " target="_blank"><img src="/v2/images/Icon-Twitch.png" alt="Go to Roll20 on Twitch" /></a> 1334 <a href="https://www.linkedin.com/company/roll20/" target="_blank"><img src="/v2/images/Icon-LinkedIn.png" alt="Go to Roll20 on LinkedIn" /></a> 1335 </div> 1336 1337 </div> 1338 <div class="footer-content"> 1339 <span class="powered-by">Powered by Roll20</span> 1340 1341 <div class="grid-container"> 1342 <div class="footer-column"> 1343 <div class="section"> 1344 <a class="section-title" href="https://roll20.net" target="_blank">Roll20 <br> The #1 Tabletop for Playing RPGs Online</a> 1345 1346 <p class="collapsed-section-title">Roll20 <br> <i>The #1 Tabletop for Playing RPGs Online</i></p> 1347 <div class="section-content"> 1348 <a href="https://roll20.net" target="_blank">Roll20 Tabletop</a> 1349 <a href="https://app.roll20.net/characters" target="_blank">Roll20 Characters</a> 1350 <a href="https://marketplace.roll20.net" target="_blank">Roll20 Marketplace</a> 1351 <a href="https://roll20.net/compendium" target="_blank">Roll20 Compendium</a> 1352 </div> 1353 </div> 1354 <div class="section"> 1355 <a class="section-title" href="https://drivethrurpg.com/" target="_blank" aria-label="Go to DriveThru Homepage">DriveThru <br> The Largest RPG Marketplace</a> 1356 <p class="collapsed-section-title">DriveThru <br> <i>The Largest RPG Marketplace</i></p> 1357 1358 <div class="section-content"> 1359 1360 <a class="homepage-link" href="https://drivethrurpg.com/" target="_blank">DriveThru</a> 1361 <a href="https://www.drivethrucomics.com/" target="_blank">DriveThruComics</a> 1362 <a href="https://www.drivethrufiction.com/index.php" target="_blank">DriveThruFiction</a> 1363 <a href="https://www.drivethrucards.com/" target="_blank">DriveThruCards</a> 1364 <a href="https://www.wargamevault.com/" target="_blank">Wargame Vault</a> 1365 </div> 1366 </div> 1367 </div> 1368 1369 <div class="footer-column"> 1370 <div class="section"> 1371 <a class="section-title" href="https://app.demiplane.com/home" target="_blank" aria-label="Go to Demiplane Homepage">Demiplane <br> Official Character Creation and Game Companion</a> 1372 <p class="collapsed-section-title">Demiplane <br> <i>Official Character Creation and Game Companion</i></p> 1373 <div class="section-content"> 1374 <a class="homepage-link" href="https://app.demiplane.com/home" target="_blank">Demiplane</a> 1375 <a href="https://app.demiplane.com/nexus/pathfinder2e" target="_blank">Pathfinder Nexus</a> 1376 <a href="https://app.demiplane.com/nexus/daggerheart" target="_blank">Daggerheart Nexus</a> 1377 <a href="https://app.demiplane.com/nexus/vampire" target="_blank">Vampire: The Masquerade Nexus</a> 1378 <a href="https://app.demiplane.com/nexus/marvelrpg" target="_blank">Marvel Multiverse RPG Nexus</a> 1379 <a href="https://app.demiplane.com/nexus/cosmererpg" target="_blank">Cosmere RPG Nexus</a> 1380 <a href="https://cyberpunkrednexus.com" target="_blank">Cyberpunk RED Nexus</a> 1381 <a href="https://app.demiplane.com/nexus/starfinder2e" target="_blank">Starfinder 2e Nexus</a> 1382 <a href="https://app.demiplane.com/nexus/traveller" target="_blank">Traveller Nexus</a> 1383 <a href="https://app.demiplane.com/nexus/alienrpg" target="_blank">ALIEN RPG Nexus</a> 1384 <a href="https://app.demiplane.com/nexus/dc20" target="_blank">DC20 Nexus</a> 1385 <a href="https://app.demiplane.com/nexus/falloutrpg" target="_blank">Fallout RPG Nexus</a> 1386 <a href="https://app.demiplane.com/nexus/candelaobscura" target="_blank">Candela Obscura Nexus</a> 1387 <a href="https://app.demiplane.com/nexus/hunter" target="_blank">Hunter: The Reckoning Nexus</a> 1388 <a href="https://app.demiplane.com/nexus/mutantyearzero" target="_blank">Mutant: Year Zero Nexus</a> 1389 <a href="https://app.demiplane.com/nexus/scion" target="_blank">Scion Nexus</a> 1390 </div> 1391 </div> 1392 </div> 1393 1394 <div class="footer-column"> 1395 <div class="section"> 1396 <a class="section-title" href="https://www.dungeonscrawl.com/" target="_blank" aria-label="Go to Dungeon Scrawl Homepage">Dungeon Scrawl <br> Create RPG Maps in Minutes</a> 1397 <p class="collapsed-section-title">Dungeon Scrawl <br> <i>Create RPG Maps in Minutes</i></p> 1398 1399 <div class="section-content"> 1400 <a class="homepage-link" href="https://www.dungeonscrawl.com/" target="_blank">Dungeon Scrawl</a> 1401 <a href="https://www.dungeonscrawl.com/" target="_blank">Create a Map</a> 1402 </div> 1403 </div> 1404 <div class="section">
1405 <span class="section-title community-content">Community Content</span> 1406 <p class="collapsed-section-title">Community Content <br> <i>Adventure by Fans Everywhere</i></p> 1407 1408 <div class="section-content"> 1409 <a href="https://www.dmsguild.com/" target="_blank">Dungeon Masters Guild (D&D)</a> 1410 <a href="https://www.pathfinderinfinite.com/" target="_blank">Pathfinder Infinite</a> 1411 <a href="https://www.starfinderinfinite.com/" target="_blank">Starfinder Infinite</a> 1412 <a href="https://www.drivethrurpg.com/cc/16/Miskatonic " target="_blank">Miskatonic Repository</a> 1413 <a href="https://www.storytellersvault.com/" target="_blank">Storytellers Vault</a> 1414 </div> 1415 </div> 1416 </div> 1417 </div> 1418 1419 </div> 1420 <div class="footer-bottom"> 1421 <div class="dice"> 1422 <img src="/v2/images/Icon-Dice.svg" alt="Dice Icon"/> 1423 </div> 1424 <div class="roll20-logos"> 1425 <div class="roll20-logo-text"> 1426 1427 <img src="/v2/images/roll20-logo.png" alt="Pink Roll20 Logo"/> 1428 1429 <p class="bottom-text">Everything for <br> your adventures</p> 1430 </div> 1431 <div class="all-logos"> 1432 1433 <img src="/v3/assets/img/logos/roll20-logo-new-light.svg" alt="Pink Roll20 Logo"/> 1434 1435 <img src="/v2/images/DriveThruRPG-LG.png" alt="DriveThru RPG Logo"/> 1436 1437 <img src="/v2/images/Demiplane-LG.png" alt="Demiplane Logo"/> 1438 1439 <img src="/v2/images/DMG-LG.png" alt="Dungeon Masters Guild Logo"/> 1440 1441 <img src="/v2/images/DungeonScrawl-LG.png" alt="Dungeown Scrawl"/> 1442 1443 </div> 1444 </div> 1445 </div> 1446</footer> 1447 1448<style> 1449#footer { 1450 padding-top: 3rem; 1451} 1452.footer-terms{ 1453 display: flex; 1454 justify-content: space-around; 1455 align-items: center; 1456 font-weight: 300; 1457 font-size: 1.3rem; 1458 line-height: 1.5rem; 1459} 1460.footer-terms span{ 1461 width: 60%; 1462 padding-left: 1rem; 1463 font-size: 1rem; 1464} 1465#footer sup { 1466/* necessary to override stylings */ 1467 top: 0; 1468 font-size: 100%; 1469} 1470 1471.powered-by { 1472 display: flex; 1473 align-items: center; 1474 text-align: center; 1475 padding: 2rem 3.5rem; 1476} 1477.powered-by::before, 1478.powered-by::after { 1479 content: ""; 1480 flex: 1; 1481 border-bottom: 1px solid rgba(98, 91, 101, 1); 1482 margin: 0 10px; 1483} 1484 1485.section-content{ 1486 display: flex; 1487 flex-direction: column; 1488 1489} 1490.section-content a{ 1491 color: var(--color-grayscale1); 1492} 1493.homepage-link{ 1494 display: none; 1495} 1496.community-content{ 1497 color: var(--color-primary-text) !important; 1498} 1499/* Large screens - 3 columns layout (2, 1, 2) */ 1500.grid-container { 1501 display: grid; 1502 grid-template-columns: 1fr 1fr 1fr; /* Three columns */ 1503 gap: 10px; 1504 align-items: start; /* Ensures items align to the top of the grid */ 1505 padding-left: 3.5rem; 1506} 1507 1508.footer-column { 1509 display: flex; 1510 flex-direction: column; 1511 gap: 16px; 1512} 1513 1514.collapsed-section-title{ 1515 display: none; 1516} 1517 1518.dice{ 1519 display: flex; 1520 align-items: center; 1521 text-align: center; 1522 justify-content: center; 1523 padding: 0 7.5rem; 1524} 1525.dice::before, 1526.dice::after { 1527 content: ""; 1528 flex: 1; 1529 border-bottom: 1px solid rgba(98, 91, 101, 1); 1530 margin: 0 10px; 1531} 1532.roll20-logo-text{ 1533 display: flex; 1534 flex-direction: column; 1535 align-items: center; 1536 justify-content: center; 1537 text-align: center; 1538} 1539.roll20-logo-text img{ 1540 height: 5rem; 1541} 1542.bottom-text{ 1543 color: var(--color-grayscale1); 1544 text-transform: uppercase; 1545 font-weight: 600; 1546} 1547.footer-bottom{ 1548 padding-bottom: 30px; 1549} 1550.roll20-logos{ 1551 padding: 2rem 0 2rem 3rem; 1552 display: flex; 1553 justify-content: space-between; 1554} 1555.all-logos{ 1556 text-align: center; 1557 width: 73%; 1558 max-width: 50rem; 1559} 1560.all-logos img{ 1561 padding-right: 1rem; 1562} 1563 1564/* There is a higher-level page break in a different CSS file, so I need to handle a specific width here when that happens */ 1565@media (max-width: 1200px) { 1566 .all-logos { 1567 width: 70%; 1568 } 1569 .all-logos img{ 1570 padding-right: 1rem; 1571 } 1572} 1573/* There is a higher-level page break in a different CSS file, so I need to handle a specific width here when that happens */ 1574@media (max-width: 991px) { 1575 .footer-terms span { 1576 width: 48%; 1577 padding-left: 10px; 1578 } 1579} 1580 1581/* Medium screens - 2 columns layout (2, 3) */ 1582@media (max-width: 1024px) { 1583 .grid-container { 1584 grid-template-columns: 1fr 1fr; /* Two equal columns */ 1585 } 1586 1587 .footer-column:nth-child(1) { 1588 grid-column: 1; 1589 grid-row: 1; 1590 } 1591 .footer-column:nth-child(2) { 1592 grid-column: 2; 1593 grid-row: 1; 1594 } 1595 .footer-column:nth-child(3) { 1596 grid-column: 2; 1597 grid-row: 2; 1598 } 1599 .footer-bottom{ 1600 padding-top: 30px; 1601 } 1602 .all-logos{ 1603 display: none; 1604 } 1605 .roll20-logos{ 1606 justify-content: center; 1607 } 1608 .dice { 1609 padding: 0; 1610 } 1611} 1612 1613/* Small screens - 1 column, collapsible sections */ 1614@media (max-width: 768px) { 1615 .section-title { 1616 display: none; 1617 } 1618 1619 .socials{ 1620 display: flex; 1621 gap: 1rem; 1622 padding-top: 1rem; 1623 } 1624 .collapsed-section-title{ 1625 display: unset; 1626 width: 100%; 1627 font-weight: 600; 1628 } 1629 .footer-terms{ 1630 flex-direction: column; 1631 1632 } 1633 .footer-terms span{ 1634 display: none; 1635 } 1636 .footer-content{ 1637 display: flex; 1638 flex-direction: column; 1639 } 1640 .grid-container { 1641 display: flex; 1642 flex-direction: column; 1643 align-self: center; 1644 width: 85%; 1645 padding-left: 0; 1646 position: relative; 1647 left: -16px; 1648 } 1649 1650 .homepage-link{ 1651 display: unset; 1652 } 1653 .globalfooter p{ 1654 color: var(--color-grayscale1); 1655 } 1656 1657 .section{ 1658 display: flex; 1659 align-items: center; 1660 justify-content: space-between; 1661 width: 100%; 1662 flex-direction: column; 1663 border-bottom: 1px solid rgba(98, 91, 101, 1); 1664 width: 110%; 1665 } 1666 1667 .section-content a{ 1668 color: var(--color-primary-text); 1669 font-size: 1rem; 1670 } 1671 1672 .footer-column { 1673 display: contents; 1674 } 1675 1676 i { 1677 font-weight: 400; 1678 } 1679 .section-content { 1680 display: none; /* Hidden by default */ 1681 } 1682 1683 .section.active .section-content { 1684 display: flex; /* Show content when active */ 1685 flex-direction: column; 1686 align-items: flex-start; 1687 width: 98%; 1688 font-size: 1.5rem; 1689 padding: 0 0 10px 5px; 1690 1691 } 1692 1693 .collapsed-section-title::after { 1694 content: '+'; 1695 float: right; 1696 } 1697 1698 .collapsed-section-title.active::after { 1699 content: '-'; 1700 transform: scaleX(1.8); 1701 } 1702 .footer-bottom{ 1703 padding-top: 20px; 1704 } 1705 1706 .powered-by { 1707 display: flex; 1708 align-items: center; 1709 text-align: center; 1710 padding: 2rem 0; 1711 } 1712 .roll20-logos{ 1713 padding: 2rem 0; 1714 } 1715} 1716 1717 1718 1719</style> 1720
1721<script> 1722 // Toggle collapsible sections on small screens 1723 document.querySelectorAll('.collapsed-section-title').forEach(section => { 1724 section.addEventListener('click', function() { 1725 this.classList.toggle('active'); 1726 this.parentElement.classList.toggle('active'); 1727 }); 1728 }); 1729</script>
1729 1730 1731 1732 1733 1734
1734<script type="text/javascript"> 1735 $(function() { 1736 var cookieName = 'roll20tempauth'; 1737 var cookieValue = Math.floor(Math.random()*100); 1738 var myDate = new Date(); 1739 myDate.setMonth(myDate.getMonth() + 1); 1740 var cookieinfo = cookieName +"=" + cookieValue + ";expires=" + myDate + ";domain=.roll20.net;path=/"; 1741 document.cookie = cookieinfo; 1742 }); 1743 </script>
1743 1744 1745
1745<script type="text/javascript"> 1746 1747 function navtumblrload(payload) { 1748 if(payload.meta.status === 200) { 1749 for(var i=0; i < payload.response.posts.length; i++) { 1750 var post = payload.response.posts[i]; 1751 var snippet = (post.body + "").split("</p>").slice(0,2).join(" "); 1752 snippet = strip_tags(snippet, "<a><br><br /><p><strong><em><b><i>"); 1753 $(".community").find(".listing").eq(i).find(".snippet").html(snippet); 1754 $(".community").find(".listing").eq(i).find("h3").text(post.title); 1755 $(".community").find(".listing").eq(i).find("a.readmore").attr('href', post.post_url); 1756 } 1757 } 1758 } 1759 1760 function strip_tags(input, allowed) { 1761 allowed = (((allowed || '') + '') 1762 .toLowerCase() 1763 .match(/<[a-z][a-z0-9]*>/g) || []) 1764 .join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>) 1765 var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, 1766 commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; 1767 return input.replace(commentsAndPhpTags, '') 1768 .replace(tags, function($0, $1) { 1769 return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; 1770 }); 1771 } 1772 $(".join-newsletter button[type=submit]").on("click", function() { 1773 var $this = $(this); 1774 $this.addClass("disabled"); 1775 $.get("/subscribe_newsletter?email=" + escape($(this).prev("input").val()), function(data) { 1776 if (data === "success") { 1777 $this.parents(".join-newsletter").find(".sub-error").hide(); 1778 $this.text("Subscribed!"); 1779 } 1780 else { 1781 $this.parents(".join-newsletter").find(".sub-error").text(data).show(); 1782 $this.removeClass("disabled"); 1783 } 1784 }); 1785 }) 1786 1787 </script>
1787 1788 1789
1789<script type="text/javascript"> 1790 $(function() { 1791 setTimeout(function(){ 1792 if($(".obs_container").length > 0) { 1793 // GA Event Tracking 1794 ga('send', { 1795 'hitType': 'event', 1796 'eventCategory': 'obsAdvert', 1797 'eventAction': 'topbar', 1798 'eventLabel': '' 1799 }); 1800 } 1801 }, 0); 1802 1803 $(".localtimeme-short").each(function() { 1804 let time = parseInt($.trim($(this).text()), 10); 1805 let d = new Date(time*1000); 1806 $(this).attr("data-timestamp", d.getTime()); 1807 1808 let mytimezone = ""; 1809 if(d.toLocaleString) { 1810 mytimezone = d.toLocaleString('en', {timeZoneName: 'short'}).split(' ').pop(); 1811 } 1812 const options = { month: 'long', hour:'numeric', minute:'numeric', day: 'numeric', timeZoneName: 'short' }; 1813 $(this).text(d.toLocaleDateString('en-EN', options)); 1814 }); 1815 }); 1816 1817 $("#roll20-top .loginform button").on("click", function() { 1818 console.log("click"); 1819 var $form = $(this).parents("form"); 1820 $form.attr("action", $form.attr("action") + "?backto=" + escape(window.location)); 1821 $form.submit(); 1822 return false; 1823 }); 1824 1825 $(".topbarnotifications .notification button").on("click", function() { 1826 var notid = $(this).attr("data-notification-id"); 1827 var othis = this; 1828 1829 $.getJSON("https://app.roll20.net/notifications/dismiss/" + notid + "/?callback=?", function(data) { 1830 if(notid == "all") { 1831 $(othis).parents(".notifications").fadeOut(); 1832 $(".alertcount").html(""); 1833 $(".topbarnotifications .dropdown-menu").remove(); 1834 $(".topbarnotifications.nav-item-messages").html("<a href='https://app.roll20.net/private_message/inbox/' class='nav-link'><i class='fas fa-envelope'></i></a>"); 1835 return; 1836 } 1837 if($(othis).attr("href")) { 1838 window.location = "https://app.roll20.net" + $(othis).attr("href"); 1839 } 1840 else { 1841 $(othis).parents(".notification").fadeOut(); 1842 var $count = $(othis).parents(".notifications").find("a .count"); 1843 var current = parseInt($.trim($count.text()), 10); 1844 current = current - 1; 1845 if(current < 0) current = 0; 1846 $count.text(current); 1847 countdown = parseInt($(".alertcount").html(), 10) 1848 $(".alertcount").html(countdown - 1); 1849 if(countdown < 2) { 1850 $(othis).parents(".notifications").fadeOut(); 1851 $(".alertcount").html(""); 1852 $(".topbarnotifications .dropdown-menu").remove(); 1853 $(".topbarnotifications.nav-item-messages").html("<a href='https://app.roll20.net/private_message/inbox/' class='nav-link'><i class='fas fa-envelope'></i></a>"); 1854 return; 1855 } 1856 } 1857 }); 1858 return false; 1859 }); 1860 1861 $(".topbarsitenotifications .newalerts").on("click", function() { 1862 1863 }); 1864 1865 $("#navbar-notifications__see-more-btn").on("click", function() { 1866 window.location = "https://app.roll20.net/sitenotifications"; 1867 }); 1868 1869 $("#menu-mobile-site-alerts").on("click", function(e) { 1870 e.preventDefault(); 1871 window.location = "https://app.roll20.net/sitenotifications"; 1872 }); 1873 1874 $("#remove-admin-menu").on("click", function() { 1875 $("#menu-adminhalla").remove(); 1876 $("ul.dropdown-menu[aria-labelledby='menu-adminhalla']").remove(); 1877 $(".logo img").prop("style", ""); 1878 }); 1879 1880 1881 if(window.dataLayer) { 1882 dataLayer.push({ 'event': 'start_pw'}); 1883 } 1884 1885</script>
1885 1886 1887
1887<script type="text/javascript"> 1888function getS3Endpoint( 1889 url, 1890) { 1891 if (!url || typeof url !== 'string') return ''; 1892 1893 return url 1894 .replace('s3.amazonaws.com/files.d20.io', 'files.d20.io') 1895 .replace('s3.amazonaws.com/files.staging.d20.io', 'files.staging.d20.io') 1896 .replace('s3.amazonaws.com/files-d20-io', 'files.d20.io') 1897 .replace('s3.amazonaws.com/files-staging-d20-io', 'files.staging.d20.io'); 1898} 1899 1900window.d20ext = window.d20ext || {}; 1901window.d20ext.utils = window.d20ext.utils || {}; 1902window.d20ext.utils.getS3Endpoint = getS3Endpoint; 1903</script>
1903 1904
1904<script type="text/javascript" src="https://cdn.roll20.net/production/blog_preview.latest.bundle.js"></script>
1904 1905 1906 1907
1907<script type="module" src="/static-fallback/nav-bar/master/assets/main-f-yrV3eN.js"></script>
1907 1908 1909</body> 1910</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.